diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 7a51028289a96..222ec24a8cf52 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: # Keep in sync with release.yaml matrix build - target: [proxy, controller, web, cni-plugin, debug, cli-bin, grafana, jaeger-webhook] + target: [proxy, controller, metrics-api, web, cni-plugin, debug, cli-bin, grafana, jaeger-webhook] name: Docker build (${{ matrix.target }}) timeout-minutes: 30 steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b573b6e91784a..b143cb940d6c8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: # Keep in sync with integration_tests.yaml matrix build - target: [proxy, controller, web, cni-plugin, debug, cli-bin, grafana, jaeger-webhook] + target: [proxy, controller, metrics-api, web, cni-plugin, debug, cli-bin, grafana, jaeger-webhook] name: Docker build (${{ matrix.target }}) timeout-minutes: 30 steps: diff --git a/bin/docker-build b/bin/docker-build index e51b84d5aff00..065582b7c7115 100755 --- a/bin/docker-build +++ b/bin/docker-build @@ -21,3 +21,5 @@ else fi "$bindir"/docker-build-grafana "$bindir"/docker-build-jaeger-webhook +"$bindir"/docker-build-metrics-api + diff --git a/bin/docker-build-metrics-api b/bin/docker-build-metrics-api new file mode 100755 index 0000000000000..5120c850901cc --- /dev/null +++ b/bin/docker-build-metrics-api @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -eu + +if [ $# -ne 0 ]; then + echo "no arguments allowed for ${0##*/}, given: $*" >&2 + exit 64 +fi + +bindir=$( cd "${BASH_SOURCE[0]%/*}" && pwd ) +rootdir=$( cd "$bindir"/.. && pwd ) + +# shellcheck source=_docker.sh +. "$bindir"/_docker.sh +# shellcheck source=_tag.sh +. "$bindir"/_tag.sh + +dockerfile=$rootdir/viz/metrics-api/Dockerfile +tag=$(head_root_tag) +docker_build metrics-api "$tag" "$dockerfile" + diff --git a/bin/docker-push b/bin/docker-push index 5fb0ac6c324b1..c48d79a65c38a 100755 --- a/bin/docker-push +++ b/bin/docker-push @@ -14,6 +14,6 @@ bindir=$( cd "${BASH_SOURCE[0]%/*}" && pwd ) # shellcheck source=_docker.sh . "$bindir"/_docker.sh -for img in cli-bin cni-plugin controller debug grafana proxy web ; do +for img in cli-bin cni-plugin controller metrics-api debug grafana proxy web ; do docker_push $img "$tag" done diff --git a/bin/fmt b/bin/fmt index 1109ea7676b3f..110ee852877ae 100755 --- a/bin/fmt +++ b/bin/fmt @@ -1,15 +1,13 @@ #!/usr/bin/env bash -set -eu +set -u -# go list will exclude the vendor directory. We use grep to exclude generated -# code. -while IFS= read -r line; do dirs+=("$line"); done <<< "$(go list -f \{\{.Dir\}\} ./... | grep -v controller/gen | grep -v viz/metrics-api/gen )" +while IFS= read -r line; do dirs+=("$line"); done <<< "$(go list -f \{\{.Dir\}\} ./...)" # go list will list all subdirectories and goimports acts recursively. This # results in certain files being reported multiple time. Therefore, we must -# dedup them. -files=$(bin/goimports -l "${dirs[@]}" | sort -u) +# dedup them. We also ignore protobuf auto-generated code. +files=$(bin/goimports -l "${dirs[@]}" | sort -u | grep -v pb.go) if [ -n "$files" ]; then for file in "${files[@]}"; do diff --git a/bin/image-load b/bin/image-load index 2f2244f5097d0..4f7a184275441 100755 --- a/bin/image-load +++ b/bin/image-load @@ -10,7 +10,7 @@ while : do case $1 in -h|--help) - echo "Load into KinD/k3d the images for Linkerd's proxy, controller, web, grafana, cli-bin, debug and cni-plugin." + echo "Load into KinD/k3d the images for Linkerd's proxy, controller, metrics-api, web, grafana, cli-bin, debug and cni-plugin." echo "" echo "Usage:" echo " bin/image-load [--kind] [--k3d] [--archive]" @@ -79,7 +79,7 @@ fi "$bin" version rm -f load_fail -for img in proxy controller web grafana cli-bin debug cni-plugin jaeger-webhook; do +for img in proxy controller web metrics-api grafana cli-bin debug cni-plugin jaeger-webhook; do printf 'Importing %s...\n' $img if [ $archive ]; then param="image-archives/$img.tar" diff --git a/bin/protoc-go.sh b/bin/protoc-go.sh index e986830b733f1..defb1a990bf8a 100755 --- a/bin/protoc-go.sh +++ b/bin/protoc-go.sh @@ -6,15 +6,17 @@ bindir=$( cd "${0%/*}" && pwd ) go install -mod=readonly github.com/golang/protobuf/protoc-gen-go -rm -rf controller/gen/public controller/gen/config viz/metrics-api/gen -mkdir -p controller/gen/public viz/metrics-api/gen/viz/healthcheck +rm -rf controller/gen/common controller/gen/public controller/gen/config viz/metrics-api/gen +mkdir -p controller/gen/common/healthcheck controller/gen/common/net controller/gen/public viz/metrics-api/gen/viz +"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/common/net.proto "$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/public.proto "$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/config/config.proto -"$bindir"/protoc -I viz/metrics-api/proto --go_out=plugins=grpc,paths=source_relative:viz/metrics-api/gen viz/metrics-api/proto/healthcheck.proto +"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/common/healthcheck.proto "$bindir"/protoc -I proto -I viz/metrics-api/proto --go_out=plugins=grpc,paths=source_relative:viz/metrics-api/gen viz/metrics-api/proto/viz.proto +mv controller/gen/common/net.pb.go controller/gen/common/net/ mv controller/gen/public.pb.go controller/gen/public/ -mv viz/metrics-api/gen/healthcheck.pb.go viz/metrics-api/gen/viz/healthcheck/healthcheck.pb.go +mv controller/gen/common/healthcheck.pb.go controller/gen/common/healthcheck/ mv viz/metrics-api/gen/viz.pb.go viz/metrics-api/gen/viz/viz.pb.go diff --git a/charts/linkerd2/README.md b/charts/linkerd2/README.md index 79753b68662bf..320da05214753 100644 --- a/charts/linkerd2/README.md +++ b/charts/linkerd2/README.md @@ -153,7 +153,6 @@ Kubernetes: `>=1.13.0-0` | global.namespace | string | `"linkerd"` | Control plane namespace | | global.podAnnotations | object | `{}` | Additional annotations to add to all pods | | global.podLabels | object | `{}` | Additional labels to add to all pods | -| global.prometheusUrl | string | `""` | url of existing prometheus | | global.proxy.cores | int | `0` | The `cpu.limit` and `cores` should be kept in sync. The value of `cores` must be an integer and should typically be set by rounding up from the limit. E.g. if cpu.limit is '1500m', cores should be 2. | | global.proxy.enableExternalProfiles | bool | `false` | Enable service profiles for non-Kubernetes services | | global.proxy.image.name | string | `"ghcr.io/linkerd/proxy"` | Docker image for the proxy | diff --git a/charts/linkerd2/templates/controller.yaml b/charts/linkerd2/templates/controller.yaml index 1554f29abe9ba..024a9202285a2 100644 --- a/charts/linkerd2/templates/controller.yaml +++ b/charts/linkerd2/templates/controller.yaml @@ -98,11 +98,6 @@ spec: - -log-level={{.Values.global.controllerLogLevel}} - -log-format={{.Values.global.controllerLogFormat}} - -cluster-domain={{.Values.global.clusterDomain}} - {{- if .Values.global.prometheusUrl }} - - -prometheus-url={{.Values.global.prometheusUrl}} - {{- else }} - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.{{.Values.global.clusterDomain}}:9090 - {{- end }} {{- include "partials.linkerd.trace" . | nindent 8 -}} image: {{.Values.controllerImage}}:{{default .Values.global.linkerdVersion .Values.global.controllerImageVersion}} imagePullPolicy: {{.Values.global.imagePullPolicy}} diff --git a/charts/linkerd2/templates/heartbeat.yaml b/charts/linkerd2/templates/heartbeat.yaml index 99102f11afc03..2b45617355315 100644 --- a/charts/linkerd2/templates/heartbeat.yaml +++ b/charts/linkerd2/templates/heartbeat.yaml @@ -47,11 +47,7 @@ spec: - "-controller-namespace={{.Values.global.namespace}}" - "-log-level={{.Values.global.controllerLogLevel}}" - "-log-format={{.Values.global.controllerLogFormat}}" - {{- if .Values.global.prometheusUrl }} - - "-prometheus-url={{.Values.global.prometheusUrl}}" - {{- else }} - "-prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.{{.Values.global.clusterDomain}}:9090" - {{- end }} {{- if .Values.heartbeatResources -}} {{- include "partials.resources" .Values.heartbeatResources | nindent 12 }} {{- end }} diff --git a/charts/linkerd2/values.yaml b/charts/linkerd2/values.yaml index 386bd9c2a1dd3..53cacad00b61a 100644 --- a/charts/linkerd2/values.yaml +++ b/charts/linkerd2/values.yaml @@ -47,8 +47,6 @@ global: # -- Trust domain used for identity identityTrustDomain: *cluster_domain - # -- url of existing prometheus - prometheusUrl: "" # -- url of external grafana instance with reverse proxy configured. grafanaUrl: "" diff --git a/cli/cmd/endpoints.go b/cli/cmd/endpoints.go index f7f7da8657447..f988567c5c1e5 100644 --- a/cli/cmd/endpoints.go +++ b/cli/cmd/endpoints.go @@ -113,7 +113,7 @@ destination.`, return cmd } -func requestEndpointsFromAPI(client public.PublicAPIClient, authorities []string) (endpointsInfo, error) { +func requestEndpointsFromAPI(client public.Client, authorities []string) (endpointsInfo, error) { info := make(endpointsInfo) // buffered channels to avoid blocking events := make(chan *destinationPb.Update, len(authorities)) diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden index bd2d2ce185752..6b756a8a44750 100644 --- a/cli/cmd/testdata/install_controlplane_tracing_output.golden +++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden @@ -1227,7 +1227,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 - -trace-collector=collector.linkerd-jaeger.svc.cluster.local:55678 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent diff --git a/cli/cmd/testdata/install_custom_domain.golden b/cli/cmd/testdata/install_custom_domain.golden index 3feb300a36718..06181a7da39fa 100644 --- a/cli/cmd/testdata/install_custom_domain.golden +++ b/cli/cmd/testdata/install_custom_domain.golden @@ -1226,7 +1226,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden index 4cd78e7ef0958..c413f68d5b401 100644 --- a/cli/cmd/testdata/install_custom_registry.golden +++ b/cli/cmd/testdata/install_custom_registry.golden @@ -1226,7 +1226,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: my.custom.registry/linkerd-io/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden index 2af449cc08be8..353ecd6fab733 100644 --- a/cli/cmd/testdata/install_default.golden +++ b/cli/cmd/testdata/install_default.golden @@ -1226,7 +1226,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_default_override_dst_get_nets.golden b/cli/cmd/testdata/install_default_override_dst_get_nets.golden index fc5c9a56be6f2..82f4b456ceb30 100644 --- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden +++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden @@ -1226,7 +1226,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden index 0042e724efb1b..97043e2b3d66f 100644 --- a/cli/cmd/testdata/install_ha_output.golden +++ b/cli/cmd/testdata/install_ha_output.golden @@ -1351,7 +1351,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden index cfb6bcb3064ef..5b9c1f287c752 100644 --- a/cli/cmd/testdata/install_ha_with_overrides_output.golden +++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden @@ -1351,7 +1351,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden index 4881007b55915..53628b28644e0 100644 --- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden +++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden @@ -1182,7 +1182,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_helm_output.golden b/cli/cmd/testdata/install_helm_output.golden index af872d573e3c5..ad735c0cd4a92 100644 --- a/cli/cmd/testdata/install_helm_output.golden +++ b/cli/cmd/testdata/install_helm_output.golden @@ -1221,7 +1221,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:linkerd-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_helm_output_ha.golden b/cli/cmd/testdata/install_helm_output_ha.golden index 3a546b9cc4d51..406af825e2021 100644 --- a/cli/cmd/testdata/install_helm_output_ha.golden +++ b/cli/cmd/testdata/install_helm_output_ha.golden @@ -1346,7 +1346,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:linkerd-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden index 3db8330aa173f..089e904ab35ac 100644 --- a/cli/cmd/testdata/install_helm_output_ha_labels.golden +++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden @@ -1358,7 +1358,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:linkerd-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden index a01ddc234bb89..4b87edc485b4e 100644 --- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden +++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden @@ -1346,7 +1346,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:linkerd-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden index 06d0203d4ebd2..872c6abe49323 100644 --- a/cli/cmd/testdata/install_no_init_container.golden +++ b/cli/cmd/testdata/install_no_init_container.golden @@ -1185,7 +1185,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden index 76b539986e1f5..cdf064dfc8997 100644 --- a/cli/cmd/testdata/install_output.golden +++ b/cli/cmd/testdata/install_output.golden @@ -1228,7 +1228,6 @@ spec: - -log-level=ControllerLogLevel - -log-format=ControllerLogFormat - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ControllerImage:ControllerImageVersion imagePullPolicy: ImagePullPolicy livenessProbe: diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden index 31a8bdf061f5a..9bd37b73f7665 100644 --- a/cli/cmd/testdata/install_proxy_ignores.golden +++ b/cli/cmd/testdata/install_proxy_ignores.golden @@ -1226,7 +1226,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=cluster.local - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/testdata/install_values_file.golden b/cli/cmd/testdata/install_values_file.golden index 1b19d513ba7bf..e0fab3161afd0 100644 --- a/cli/cmd/testdata/install_values_file.golden +++ b/cli/cmd/testdata/install_values_file.golden @@ -1211,7 +1211,6 @@ spec: - -log-level=info - -log-format=plain - -cluster-domain=example.com - - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.example.com:9090 image: ghcr.io/linkerd/controller:install-control-plane-version imagePullPolicy: IfNotPresent livenessProbe: diff --git a/cli/cmd/version.go b/cli/cmd/version.go index dc42fc10094fd..82a50c21f2517 100644 --- a/cli/cmd/version.go +++ b/cli/cmd/version.go @@ -12,8 +12,8 @@ import ( "github.com/linkerd/linkerd2/pkg/k8s" api "github.com/linkerd/linkerd2/pkg/public" "github.com/linkerd/linkerd2/pkg/version" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const defaultVersionString = "unavailable" @@ -51,7 +51,7 @@ func newCmdVersion() *cobra.Command { } } - configureAndRunVersion(cmd.Context(), k8sAPI, options, os.Stdout, api.RawPublicAPIClient, api.RawVizAPIClient) + configureAndRunVersion(cmd.Context(), k8sAPI, options, os.Stdout, api.RawPublicAPIClient) return nil }, } @@ -70,7 +70,6 @@ func configureAndRunVersion( options *versionOptions, stdout io.Writer, mkPublicClient func(ctx context.Context, k8sAPI *k8s.KubernetesAPI, controlPlaneNamespace, apiAddr string) (publicPb.ApiClient, error), - mkVizClient func(ctx context.Context, k8sAPI *k8s.KubernetesAPI, controlPlaneNamespace, apiAddr string) (pb.ApiClient, error), ) { clientVersion := version.Version if options.shortVersion { @@ -99,38 +98,23 @@ func configureAndRunVersion( } if options.proxy { - vizClient, clientErr := mkVizClient(ctx, k8sAPI, controlPlaneNamespace, apiAddr) - if clientErr != nil { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + selector := fmt.Sprintf("%s=%s", k8s.ControllerNSLabel, controlPlaneNamespace) + podList, err := k8sAPI.CoreV1().Pods(options.namespace).List(ctx, metav1.ListOptions{LabelSelector: selector}) + if err != nil { fmt.Fprintln(stdout, "Proxy versions: unavailable") } else { - req := &pb.ListPodsRequest{} - if options.namespace != "" { - req.Selector = &pb.ResourceSelection{ - Resource: &pb.Resource{ - Namespace: options.namespace, - }, - } + counts := make(map[string]int) + for _, pod := range podList.Items { + counts[k8s.GetProxyVersion(pod)]++ } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - resp, err := vizClient.ListPods(ctx, req) - if err != nil { + if len(counts) == 0 { fmt.Fprintln(stdout, "Proxy versions: unavailable") } else { - counts := make(map[string]int) - for _, pod := range resp.GetPods() { - if pod.ControllerNamespace == controlPlaneNamespace { - counts[pod.GetProxyVersion()]++ - } - } - if len(counts) == 0 { - fmt.Fprintln(stdout, "Proxy versions: unavailable") - } else { - fmt.Fprintln(stdout, "Proxy versions:") - for version, count := range counts { - fmt.Fprintf(stdout, "\t%s (%d pods)\n", version, count) - } + fmt.Fprintln(stdout, "Proxy versions:") + for version, count := range counts { + fmt.Fprintf(stdout, "\t%s (%d pods)\n", version, count) } } } diff --git a/cli/cmd/version_test.go b/cli/cmd/version_test.go index 2194495724a5c..09c4efb38f5d3 100644 --- a/cli/cmd/version_test.go +++ b/cli/cmd/version_test.go @@ -67,7 +67,7 @@ func TestConfigureAndRunVersion(t *testing.T) { t.Run(fmt.Sprintf("test %d TestConfigureAndRunVersion()", i), func(t *testing.T) { wout := bytes.NewBufferString("") - configureAndRunVersion(context.Background(), nil, tc.options, wout, tc.mkClient, nil) + configureAndRunVersion(context.Background(), nil, tc.options, wout, tc.mkClient) if tc.out != wout.String() { t.Fatalf("Expected output: \"%s\", got: \"%s\"", tc.out, wout) diff --git a/controller/Dockerfile b/controller/Dockerfile index 39dfd3d16d690..286b6f26990fa 100644 --- a/controller/Dockerfile +++ b/controller/Dockerfile @@ -13,7 +13,7 @@ RUN ./bin/install-deps $TARGETARCH FROM go-deps as golang WORKDIR /linkerd-build COPY controller/gen controller/gen -# TODO: remove when viz API code gets moved to /viz +# TODO: remove when tap code gets moved to /viz COPY viz/metrics-api/gen/viz viz/metrics-api/gen/viz COPY pkg pkg COPY controller controller diff --git a/controller/api/public/client.go b/controller/api/public/client.go index eff4f5c29063b..cf1b305776b86 100644 --- a/controller/api/public/client.go +++ b/controller/api/public/client.go @@ -14,13 +14,9 @@ import ( publicPb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/protohttp" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" log "github.com/sirupsen/logrus" "go.opencensus.io/plugin/ochttp" "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) const ( @@ -31,81 +27,24 @@ const ( apiDeployment = "linkerd-controller" ) -// PublicAPIClient wraps one gRPC client interface for publicPb.Api: -// TODO: remove stuttering name when viz api code moves to /viz -// nolint -type PublicAPIClient interface { +// Client wraps one gRPC client interface for publicPb.Api: +type Client interface { publicPb.ApiClient destinationPb.DestinationClient } -// VizAPIClient wraps one gRPC client interface for pb.Api: -type VizAPIClient interface { - pb.ApiClient -} - type grpcOverHTTPClient struct { serverURL *url.URL httpClient *http.Client controlPlaneNamespace string } -func (c *grpcOverHTTPClient) StatSummary(ctx context.Context, req *pb.StatSummaryRequest, _ ...grpc.CallOption) (*pb.StatSummaryResponse, error) { - var msg pb.StatSummaryResponse - err := c.apiRequest(ctx, "StatSummary", req, &msg) - return &msg, err -} - -func (c *grpcOverHTTPClient) Edges(ctx context.Context, req *pb.EdgesRequest, _ ...grpc.CallOption) (*pb.EdgesResponse, error) { - var msg pb.EdgesResponse - err := c.apiRequest(ctx, "Edges", req, &msg) - return &msg, err -} - -func (c *grpcOverHTTPClient) TopRoutes(ctx context.Context, req *pb.TopRoutesRequest, _ ...grpc.CallOption) (*pb.TopRoutesResponse, error) { - var msg pb.TopRoutesResponse - err := c.apiRequest(ctx, "TopRoutes", req, &msg) - return &msg, err -} - -func (c *grpcOverHTTPClient) Gateways(ctx context.Context, req *pb.GatewaysRequest, _ ...grpc.CallOption) (*pb.GatewaysResponse, error) { - var msg pb.GatewaysResponse - err := c.apiRequest(ctx, "Gateways", req, &msg) - return &msg, err -} - func (c *grpcOverHTTPClient) Version(ctx context.Context, req *publicPb.Empty, _ ...grpc.CallOption) (*publicPb.VersionInfo, error) { var msg publicPb.VersionInfo err := c.apiRequest(ctx, "Version", req, &msg) return &msg, err } -func (c *grpcOverHTTPClient) SelfCheck(ctx context.Context, req *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) { - var msg healthcheckPb.SelfCheckResponse - err := c.apiRequest(ctx, "SelfCheck", req, &msg) - return &msg, err -} - -func (c *grpcOverHTTPClient) ListPods(ctx context.Context, req *pb.ListPodsRequest, _ ...grpc.CallOption) (*pb.ListPodsResponse, error) { - var msg pb.ListPodsResponse - err := c.apiRequest(ctx, "ListPods", req, &msg) - return &msg, err -} - -func (c *grpcOverHTTPClient) ListServices(ctx context.Context, req *pb.ListServicesRequest, _ ...grpc.CallOption) (*pb.ListServicesResponse, error) { - var msg pb.ListServicesResponse - err := c.apiRequest(ctx, "ListServices", req, &msg) - return &msg, err -} - -func (c *grpcOverHTTPClient) Tap(ctx context.Context, req *pb.TapRequest, _ ...grpc.CallOption) (pb.Api_TapClient, error) { - return nil, status.Error(codes.Unimplemented, "Tap is deprecated in public API, use tap APIServer") -} - -func (c *grpcOverHTTPClient) TapByResource(ctx context.Context, req *pb.TapByResourceRequest, _ ...grpc.CallOption) (pb.Api_TapByResourceClient, error) { - return nil, status.Error(codes.Unimplemented, "Tap is deprecated in public API, use tap APIServer") -} - func (c *grpcOverHTTPClient) Get(ctx context.Context, req *destinationPb.GetDestination, _ ...grpc.CallOption) (destinationPb.Destination_GetClient, error) { url := c.endpointNameToPublicAPIURL("DestinationGet") httpRsp, err := c.post(ctx, url, req) @@ -184,7 +123,7 @@ func (c destinationClient) Recv() (*destinationPb.Update, error) { return &msg, err } -func newClient(apiURL *url.URL, httpClientToUse *http.Client, controlPlaneNamespace string) (VizAPIClient, error) { +func newClient(apiURL *url.URL, httpClientToUse *http.Client, controlPlaneNamespace string) (Client, error) { if !apiURL.IsAbs() { return nil, fmt.Errorf("server URL must be absolute, was [%s]", apiURL.String()) } @@ -200,17 +139,9 @@ func newClient(apiURL *url.URL, httpClientToUse *http.Client, controlPlaneNamesp }, nil } -func newPublicClient(apiURL *url.URL, httpClientToUse *http.Client, controlPlaneNamespace string) (PublicAPIClient, error) { - client, err := newClient(apiURL, httpClientToUse, controlPlaneNamespace) - if err != nil { - return nil, err - } - return client.(PublicAPIClient), nil -} - -// NewInternalClient creates a new Viz API client intended to run inside a +// NewInternalClient creates a new public API client intended to run inside a // Kubernetes cluster. -func NewInternalClient(controlPlaneNamespace string, kubeAPIHost string) (VizAPIClient, error) { +func NewInternalClient(controlPlaneNamespace string, kubeAPIHost string) (Client, error) { apiURL, err := url.Parse(fmt.Sprintf("http://%s/", kubeAPIHost)) if err != nil { return nil, err @@ -219,19 +150,9 @@ func NewInternalClient(controlPlaneNamespace string, kubeAPIHost string) (VizAPI return newClient(apiURL, &http.Client{Transport: &ochttp.Transport{}}, controlPlaneNamespace) } -// NewInternalPublicClient creates a new Public API client intended to run inside a -// Kubernetes cluster. -func NewInternalPublicClient(controlPlaneNamespace string, kubeAPIHost string) (PublicAPIClient, error) { - client, err := NewInternalClient(controlPlaneNamespace, kubeAPIHost) - if err != nil { - return nil, err - } - return client.(PublicAPIClient), nil -} - -// NewExternalClient creates a new Viz API client intended to run from +// NewExternalClient creates a new public API client intended to run from // outside a Kubernetes cluster. -func NewExternalClient(ctx context.Context, controlPlaneNamespace string, kubeAPI *k8s.KubernetesAPI) (VizAPIClient, error) { +func NewExternalClient(ctx context.Context, controlPlaneNamespace string, kubeAPI *k8s.KubernetesAPI) (Client, error) { portforward, err := k8s.NewPortForward( ctx, kubeAPI, @@ -262,13 +183,3 @@ func NewExternalClient(ctx context.Context, controlPlaneNamespace string, kubeAP return newClient(apiURL, httpClientToUse, controlPlaneNamespace) } - -// NewExternalPublicClient creates a new Public API client intended to run from -// outside a Kubernetes cluster. -func NewExternalPublicClient(ctx context.Context, controlPlaneNamespace string, kubeAPI *k8s.KubernetesAPI) (PublicAPIClient, error) { - client, err := NewExternalClient(ctx, controlPlaneNamespace, kubeAPI) - if err != nil { - return nil, err - } - return client.(PublicAPIClient), nil -} diff --git a/controller/api/public/client_test.go b/controller/api/public/client_test.go index a954144215f46..11b5faacf55fc 100644 --- a/controller/api/public/client_test.go +++ b/controller/api/public/client_test.go @@ -43,7 +43,7 @@ func TestNewInternalClient(t *testing.T) { Host: "some-hostname", Path: "/", } - client, err := newPublicClient(apiURL, mockHTTPClient, "linkerd") + client, err := newClient(apiURL, mockHTTPClient, "linkerd") if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/controller/api/public/grpc_server.go b/controller/api/public/grpc_server.go index be4200cf094b1..0a67abcac3294 100644 --- a/controller/api/public/grpc_server.go +++ b/controller/api/public/grpc_server.go @@ -3,79 +3,41 @@ package public import ( "context" "errors" - "fmt" "runtime" - "time" - "github.com/golang/protobuf/ptypes/duration" destinationPb "github.com/linkerd/linkerd2-proxy-api/go/destination" - "github.com/linkerd/linkerd2/controller/api/util" - publicPb "github.com/linkerd/linkerd2/controller/gen/public" + pb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/controller/k8s" - pkgK8s "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/prometheus" "github.com/linkerd/linkerd2/pkg/version" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" - promv1 "github.com/prometheus/client_golang/api/prometheus/v1" log "github.com/sirupsen/logrus" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" ) -// PublicAPIServer specifies the interface the Public API server should implement -// TODO: remove stuttering name when viz api code moves to /viz -// nolint -type PublicAPIServer interface { - publicPb.ApiServer - destinationPb.DestinationServer -} - -// VizAPIServer specifies the interface the Viz metric API server should implement -type VizAPIServer interface { +// Server specifies the interface the Public API server should implement +type Server interface { pb.ApiServer + destinationPb.DestinationServer } type grpcServer struct { - prometheusAPI promv1.API destinationClient destinationPb.DestinationClient k8sAPI *k8s.API controllerNamespace string clusterDomain string - ignoredNamespaces []string -} - -type podReport struct { - lastReport time.Time - processStartTimeSeconds time.Time } -const ( - podQuery = "max(process_start_time_seconds{%s}) by (pod, namespace)" - k8sClientSubsystemName = "kubernetes" - k8sClientCheckDescription = "control plane can talk to Kubernetes" - // promClientSubsystemName = "prometheus" - // promClientCheckDescription = "control plane can talk to Prometheus" -) - func newGrpcServer( - promAPI promv1.API, destinationClient destinationPb.DestinationClient, k8sAPI *k8s.API, controllerNamespace string, clusterDomain string, - ignoredNamespaces []string, ) *grpcServer { grpcServer := &grpcServer{ - prometheusAPI: promAPI, destinationClient: destinationClient, k8sAPI: k8sAPI, controllerNamespace: controllerNamespace, clusterDomain: clusterDomain, - ignoredNamespaces: ignoredNamespaces, } pb.RegisterApiServer(prometheus.NewGrpcServer(), grpcServer) @@ -83,164 +45,8 @@ func newGrpcServer( return grpcServer } -func (*grpcServer) Version(ctx context.Context, req *publicPb.Empty) (*publicPb.VersionInfo, error) { - return &publicPb.VersionInfo{GoVersion: runtime.Version(), ReleaseVersion: version.Version, BuildDate: "1970-01-01T00:00:00Z"}, nil -} - -func (s *grpcServer) ListPods(ctx context.Context, req *pb.ListPodsRequest) (*pb.ListPodsResponse, error) { - log.Debugf("ListPods request: %+v", req) - - targetOwner := req.GetSelector().GetResource() - - // Reports is a map from instance name to the absolute time of the most recent - // report from that instance and its process start time - reports := make(map[string]podReport) - - if req.GetNamespace() != "" && req.GetSelector() != nil { - return nil, errors.New("cannot set both namespace and resource in the request. These are mutually exclusive") - } - - labelSelector := labels.Everything() - if s := req.GetSelector().GetLabelSelector(); s != "" { - var err error - labelSelector, err = labels.Parse(s) - if err != nil { - return nil, fmt.Errorf("invalid label selector \"%s\": %s", s, err) - } - } - - nsQuery := "" - namespace := "" - if req.GetNamespace() != "" { - namespace = req.GetNamespace() - } else if targetOwner.GetNamespace() != "" { - namespace = targetOwner.GetNamespace() - } else if targetOwner.GetType() == pkgK8s.Namespace { - namespace = targetOwner.GetName() - } - if namespace != "" { - nsQuery = fmt.Sprintf("namespace=\"%s\"", namespace) - } - processStartTimeQuery := fmt.Sprintf(podQuery, nsQuery) - - // Query Prometheus for all pods present - vec, err := s.queryProm(ctx, processStartTimeQuery) - if err != nil && !errors.Is(err, ErrNoPrometheusInstance) { - return nil, err - } - - for _, sample := range vec { - pod := string(sample.Metric["pod"]) - timestamp := sample.Timestamp - - reports[pod] = podReport{ - lastReport: time.Unix(0, int64(timestamp)*int64(time.Millisecond)), - processStartTimeSeconds: time.Unix(0, int64(sample.Value)*int64(time.Second)), - } - } - - var pods []*corev1.Pod - if namespace != "" { - pods, err = s.k8sAPI.Pod().Lister().Pods(namespace).List(labelSelector) - } else { - pods, err = s.k8sAPI.Pod().Lister().List(labelSelector) - } - - if err != nil { - return nil, err - } - podList := make([]*pb.Pod, 0) - - for _, pod := range pods { - if s.shouldIgnore(pod) { - continue - } - - ownerKind, ownerName := s.k8sAPI.GetOwnerKindAndName(ctx, pod, false) - // filter out pods without matching owner - if targetOwner.GetNamespace() != "" && targetOwner.GetNamespace() != pod.GetNamespace() { - continue - } - if targetOwner.GetType() != "" && targetOwner.GetType() != ownerKind { - continue - } - if targetOwner.GetName() != "" && targetOwner.GetName() != ownerName { - continue - } - - updated, added := reports[pod.Name] - - item := util.K8sPodToPublicPod(*pod, ownerKind, ownerName) - item.Added = added - - if added { - since := time.Since(updated.lastReport) - item.SinceLastReport = &duration.Duration{ - Seconds: int64(since / time.Second), - Nanos: int32(since % time.Second), - } - sinceStarting := time.Since(updated.processStartTimeSeconds) - item.Uptime = &duration.Duration{ - Seconds: int64(sinceStarting / time.Second), - Nanos: int32(sinceStarting % time.Second), - } - } - - podList = append(podList, item) - } - - rsp := pb.ListPodsResponse{Pods: podList} - - log.Debugf("ListPods response: %s", rsp.String()) - - return &rsp, nil -} - -func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) { - k8sClientCheck := &healthcheckPb.CheckResult{ - SubsystemName: k8sClientSubsystemName, - CheckDescription: k8sClientCheckDescription, - Status: healthcheckPb.CheckStatus_OK, - } - _, err := s.k8sAPI.Pod().Lister().List(labels.Everything()) - if err != nil { - k8sClientCheck.Status = healthcheckPb.CheckStatus_ERROR - k8sClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling the Kubernetes API: %s", err) - } - - response := &healthcheckPb.SelfCheckResponse{ - Results: []*healthcheckPb.CheckResult{ - k8sClientCheck, - }, - } - - // TODO: viz: Enable this check once controller moves to viz - /* - if s.prometheusAPI != nil { - promClientCheck := &healthcheckPb.CheckResult{ - SubsystemName: promClientSubsystemName, - CheckDescription: promClientCheckDescription, - Status: healthcheckPb.CheckStatus_OK, - } - _, err = s.queryProm(ctx, fmt.Sprintf(podQuery, "")) - if err != nil { - promClientCheck.Status = healthcheckPb.CheckStatus_ERROR - promClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling Prometheus from the control plane: %s", err) - } - - response.Results = append(response.Results, promClientCheck) - } - */ - - return response, nil -} - -func (s *grpcServer) Tap(req *pb.TapRequest, stream pb.Api_TapServer) error { - return status.Error(codes.Unimplemented, "Tap is deprecated in public API, use tap APIServer") -} - -func (s *grpcServer) TapByResource(req *pb.TapByResourceRequest, stream pb.Api_TapByResourceServer) error { - return status.Error(codes.Unimplemented, "Tap is deprecated in public API, use tap APIServer") +func (*grpcServer) Version(ctx context.Context, req *pb.Empty) (*pb.VersionInfo, error) { + return &pb.VersionInfo{GoVersion: runtime.Version(), ReleaseVersion: version.Version, BuildDate: "1970-01-01T00:00:00Z"}, nil } // Pass through to Destination service @@ -269,31 +75,3 @@ func (s *grpcServer) GetProfile(_ *destinationPb.GetDestination, _ destinationPb // Not implemented in the Public API. Instead, the proxies should reach the Destination gRPC server directly. return errors.New("Not implemented") } - -func (s *grpcServer) shouldIgnore(pod *corev1.Pod) bool { - for _, namespace := range s.ignoredNamespaces { - if pod.Namespace == namespace { - return true - } - } - return false -} - -func (s *grpcServer) ListServices(ctx context.Context, req *pb.ListServicesRequest) (*pb.ListServicesResponse, error) { - log.Debugf("ListServices request: %+v", req) - - services, err := s.k8sAPI.GetServices(req.Namespace, "") - if err != nil { - return nil, err - } - - svcs := make([]*pb.Service, 0) - for _, svc := range services { - svcs = append(svcs, &pb.Service{ - Name: svc.GetName(), - Namespace: svc.GetNamespace(), - }) - } - - return &pb.ListServicesResponse{Services: svcs}, nil -} diff --git a/controller/api/public/http_server.go b/controller/api/public/http_server.go index e5a8fe0a35b07..3faa739f2efb1 100644 --- a/controller/api/public/http_server.go +++ b/controller/api/public/http_server.go @@ -7,33 +7,21 @@ import ( "github.com/golang/protobuf/proto" destinationPb "github.com/linkerd/linkerd2-proxy-api/go/destination" - publicPb "github.com/linkerd/linkerd2/controller/gen/public" + pb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/controller/k8s" "github.com/linkerd/linkerd2/pkg/prometheus" "github.com/linkerd/linkerd2/pkg/protohttp" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" - promApi "github.com/prometheus/client_golang/api" - promv1 "github.com/prometheus/client_golang/api/prometheus/v1" log "github.com/sirupsen/logrus" "google.golang.org/grpc/metadata" ) var ( - gatewaysPath = fullURLPathFor("Gateways") - statSummaryPath = fullURLPathFor("StatSummary") - topRoutesPath = fullURLPathFor("TopRoutes") - versionPath = fullURLPathFor("Version") - listPodsPath = fullURLPathFor("ListPods") - listServicesPath = fullURLPathFor("ListServices") - selfCheckPath = fullURLPathFor("SelfCheck") - edgesPath = fullURLPathFor("Edges") - destGetPath = fullURLPathFor("DestinationGet") + versionPath = fullURLPathFor("Version") + destGetPath = fullURLPathFor("DestinationGet") ) type handler struct { - publicGRPCServer PublicAPIServer - grpcServer VizAPIServer + grpcServer Server } func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { @@ -48,22 +36,8 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Serve request switch req.URL.Path { - case gatewaysPath: - h.handleGateways(w, req) - case statSummaryPath: - h.handleStatSummary(w, req) - case topRoutesPath: - h.handleTopRoutes(w, req) case versionPath: h.handleVersion(w, req) - case listPodsPath: - h.handleListPods(w, req) - case listServicesPath: - h.handleListServices(w, req) - case selfCheckPath: - h.handleSelfCheck(w, req) - case edgesPath: - h.handleEdges(w, req) case destGetPath: h.handleDestGet(w, req) default: @@ -72,163 +46,15 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } -func (h *handler) handleGateways(w http.ResponseWriter, req *http.Request) { - var protoRequest pb.GatewaysRequest - - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.grpcServer.Gateways(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - -func (h *handler) handleStatSummary(w http.ResponseWriter, req *http.Request) { - var protoRequest pb.StatSummaryRequest - - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.grpcServer.StatSummary(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - -func (h *handler) handleEdges(w http.ResponseWriter, req *http.Request) { - var protoRequest pb.EdgesRequest - - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.grpcServer.Edges(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - -func (h *handler) handleTopRoutes(w http.ResponseWriter, req *http.Request) { - var protoRequest pb.TopRoutesRequest - - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.grpcServer.TopRoutes(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - func (h *handler) handleVersion(w http.ResponseWriter, req *http.Request) { - var protoRequest publicPb.Empty - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.publicGRPCServer.Version(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - -func (h *handler) handleSelfCheck(w http.ResponseWriter, req *http.Request) { - var protoRequest healthcheckPb.SelfCheckRequest - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.grpcServer.SelfCheck(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - -func (h *handler) handleListPods(w http.ResponseWriter, req *http.Request) { - var protoRequest pb.ListPodsRequest - err := protohttp.HTTPRequestToProto(req, &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - rsp, err := h.grpcServer.ListPods(req.Context(), &protoRequest) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } - - err = protohttp.WriteProtoToHTTPResponse(w, rsp) - if err != nil { - protohttp.WriteErrorToHTTPResponse(w, err) - return - } -} - -func (h *handler) handleListServices(w http.ResponseWriter, req *http.Request) { - var protoRequest pb.ListServicesRequest - + var protoRequest pb.Empty err := protohttp.HTTPRequestToProto(req, &protoRequest) if err != nil { protohttp.WriteErrorToHTTPResponse(w, err) return } - rsp, err := h.grpcServer.ListServices(req.Context(), &protoRequest) + rsp, err := h.grpcServer.Version(req.Context(), &protoRequest) if err != nil { protohttp.WriteErrorToHTTPResponse(w, err) return @@ -256,7 +82,7 @@ func (h *handler) handleDestGet(w http.ResponseWriter, req *http.Request) { } server := destinationServer{streamServer{w: flushableWriter, req: req}} - err = h.publicGRPCServer.Get(&protoRequest, server) + err = h.grpcServer.Get(&protoRequest, server) if err != nil { protohttp.WriteErrorToHTTPResponse(w, err) return @@ -302,30 +128,19 @@ func fullURLPathFor(method string) string { // NewServer creates a Public API HTTP server. func NewServer( addr string, - prometheusClient promApi.Client, destinationClient destinationPb.DestinationClient, k8sAPI *k8s.API, controllerNamespace string, clusterDomain string, - ignoredNamespaces []string, ) *http.Server { - var promAPI promv1.API - if prometheusClient != nil { - promAPI = promv1.NewAPI(prometheusClient) - } - - grpcServer := newGrpcServer( - promAPI, - destinationClient, - k8sAPI, - controllerNamespace, - clusterDomain, - ignoredNamespaces, - ) baseHandler := &handler{ - publicGRPCServer: grpcServer, - grpcServer: grpcServer, + grpcServer: newGrpcServer( + destinationClient, + k8sAPI, + controllerNamespace, + clusterDomain, + ), } instrumentedHandler := prometheus.WithTelemetry(baseHandler) diff --git a/controller/api/public/http_server_test.go b/controller/api/public/http_server_test.go index 08805cd17df34..f1d1eeb261645 100644 --- a/controller/api/public/http_server_test.go +++ b/controller/api/public/http_server_test.go @@ -10,8 +10,6 @@ import ( "github.com/golang/protobuf/proto" destinationPb "github.com/linkerd/linkerd2-proxy-api/go/destination" publicPb "github.com/linkerd/linkerd2/controller/gen/public" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" ) type mockServer struct { @@ -26,66 +24,11 @@ type mockGrpcServer struct { DestinationStreamsToReturn []*destinationPb.Update } -func (m *mockGrpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest) (*pb.StatSummaryResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*pb.StatSummaryResponse), m.ErrorToReturn -} - -func (m *mockGrpcServer) Gateways(ctx context.Context, req *pb.GatewaysRequest) (*pb.GatewaysResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*pb.GatewaysResponse), m.ErrorToReturn -} - -func (m *mockGrpcServer) TopRoutes(ctx context.Context, req *pb.TopRoutesRequest) (*pb.TopRoutesResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*pb.TopRoutesResponse), m.ErrorToReturn -} - -func (m *mockGrpcServer) Edges(ctx context.Context, req *pb.EdgesRequest) (*pb.EdgesResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*pb.EdgesResponse), m.ErrorToReturn -} - func (m *mockGrpcServer) Version(ctx context.Context, req *publicPb.Empty) (*publicPb.VersionInfo, error) { m.LastRequestReceived = req return m.ResponseToReturn.(*publicPb.VersionInfo), m.ErrorToReturn } -func (m *mockGrpcServer) ListPods(ctx context.Context, req *pb.ListPodsRequest) (*pb.ListPodsResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*pb.ListPodsResponse), m.ErrorToReturn -} - -func (m *mockGrpcServer) ListServices(ctx context.Context, req *pb.ListServicesRequest) (*pb.ListServicesResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*pb.ListServicesResponse), m.ErrorToReturn -} - -func (m *mockGrpcServer) SelfCheck(ctx context.Context, req *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) { - m.LastRequestReceived = req - return m.ResponseToReturn.(*healthcheckPb.SelfCheckResponse), m.ErrorToReturn -} - -func (m *mockGrpcServer) Tap(req *pb.TapRequest, tapServer pb.Api_TapServer) error { - m.LastRequestReceived = req - if m.ErrorToReturn != nil { - // Not implemented in public API. Instead, use tap APIServer. - return errors.New("Not implemented") - } - - return m.ErrorToReturn -} - -func (m *mockGrpcServer) TapByResource(req *pb.TapByResourceRequest, tapServer pb.Api_TapByResourceServer) error { - m.LastRequestReceived = req - if m.ErrorToReturn != nil { - // Not implemented in public API. Instead, use tap APIServer. - return errors.New("Not implemented") - } - - return m.ErrorToReturn -} - func (m *mockGrpcServer) Get(req *destinationPb.GetDestination, destinationServer destinationPb.Destination_GetServer) error { m.LastRequestReceived = req if m.ErrorToReturn == nil { @@ -110,30 +53,6 @@ type grpcCallTestCase struct { func TestServer(t *testing.T) { t.Run("Delegates all non-streaming RPC messages to the underlying grpc server", func(t *testing.T) { - mockGrpcServer, client := getServerVizClient(t) - - listPodsReq := &pb.ListPodsRequest{} - testListPods := grpcCallTestCase{ - expectedRequest: listPodsReq, - expectedResponse: &pb.ListPodsResponse{ - Pods: []*pb.Pod{ - {Status: "ok-ish"}, - }, - }, - functionCall: func() (proto.Message, error) { return client.ListPods(context.TODO(), listPodsReq) }, - } - - statSummaryReq := &pb.StatSummaryRequest{} - testStatSummary := grpcCallTestCase{ - expectedRequest: statSummaryReq, - expectedResponse: &pb.StatSummaryResponse{}, - functionCall: func() (proto.Message, error) { return client.StatSummary(context.TODO(), statSummaryReq) }, - } - - for _, testCase := range []grpcCallTestCase{testListPods, testStatSummary} { - assertCallWasForwarded(t, &mockGrpcServer.mockServer, testCase.expectedRequest, testCase.expectedResponse, testCase.functionCall) - } - mockGrpcServer, clientPublic := getServerPublicClient(t) versionReq := &publicPb.Empty{} @@ -207,58 +126,9 @@ func TestServer(t *testing.T) { } } }) - - t.Run("Handles Tap route errors before opening keep-alive response", func(t *testing.T) { - mockGrpcServer, client := getServerVizClient(t) - - mockGrpcServer.ErrorToReturn = errors.New("expected error") - - _, err := client.Tap(context.TODO(), &pb.TapRequest{}) - if err == nil { - t.Fatalf("Expecting error, got nothing") - } - }) - - t.Run("Handles TapByResource route errors before opening keep-alive response", func(t *testing.T) { - mockGrpcServer, client := getServerVizClient(t) - - mockGrpcServer.ErrorToReturn = errors.New("expected error") - - _, err := client.TapByResource(context.TODO(), &pb.TapByResourceRequest{}) - if err == nil { - t.Fatalf("Expecting error, got nothing") - } - }) - -} - -func getServerPublicClient(t *testing.T) (*mockGrpcServer, PublicAPIClient) { - mockGrpcServer := &mockGrpcServer{} - - listener, err := net.Listen("tcp", "localhost:0") - if err != nil { - t.Fatalf("Could not start listener: %v", err) - } - - go func() { - handler := &handler{ - publicGRPCServer: mockGrpcServer, - } - err := http.Serve(listener, handler) - if err != nil { - t.Fatalf("Could not start server: %v", err) - } - }() - - client, err := NewInternalPublicClient("linkerd", listener.Addr().String()) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - return mockGrpcServer, client } -func getServerVizClient(t *testing.T) (*mockGrpcServer, VizAPIClient) { +func getServerPublicClient(t *testing.T) (*mockGrpcServer, Client) { mockGrpcServer := &mockGrpcServer{} listener, err := net.Listen("tcp", "localhost:0") diff --git a/controller/api/public/test_helper.go b/controller/api/public/test_helper.go index 0efe8a8a4b6da..d74b6ace5e0f9 100644 --- a/controller/api/public/test_helper.go +++ b/controller/api/public/test_helper.go @@ -3,19 +3,13 @@ package public import ( "context" "errors" - "fmt" "io" - "reflect" - "sort" "sync" "time" destinationPb "github.com/linkerd/linkerd2-proxy-api/go/destination" "github.com/linkerd/linkerd2-proxy-api/go/net" publicPb "github.com/linkerd/linkerd2/controller/gen/public" - "github.com/linkerd/linkerd2/controller/k8s" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" promv1 "github.com/prometheus/client_golang/api/prometheus/v1" "github.com/prometheus/common/model" "google.golang.org/grpc" @@ -23,38 +17,9 @@ import ( // MockAPIClient satisfies the Public API's gRPC interfaces (public.APIClient). type MockAPIClient struct { - ErrorToReturn error - VersionInfoToReturn *publicPb.VersionInfo - ListPodsResponseToReturn *pb.ListPodsResponse - ListServicesResponseToReturn *pb.ListServicesResponse - StatSummaryResponseToReturn *pb.StatSummaryResponse - GatewaysResponseToReturn *pb.GatewaysResponse - TopRoutesResponseToReturn *pb.TopRoutesResponse - EdgesResponseToReturn *pb.EdgesResponse - SelfCheckResponseToReturn *healthcheckPb.SelfCheckResponse - APITapClientToReturn pb.Api_TapClient - APITapByResourceClientToReturn pb.Api_TapByResourceClient - DestinationGetClientToReturn destinationPb.Destination_GetClient -} - -// StatSummary provides a mock of a Public API method. -func (c *MockAPIClient) StatSummary(ctx context.Context, in *pb.StatSummaryRequest, opts ...grpc.CallOption) (*pb.StatSummaryResponse, error) { - return c.StatSummaryResponseToReturn, c.ErrorToReturn -} - -// Gateways provides a mock of a Public API method. -func (c *MockAPIClient) Gateways(ctx context.Context, in *pb.GatewaysRequest, opts ...grpc.CallOption) (*pb.GatewaysResponse, error) { - return c.GatewaysResponseToReturn, c.ErrorToReturn -} - -// TopRoutes provides a mock of a Public API method. -func (c *MockAPIClient) TopRoutes(ctx context.Context, in *pb.TopRoutesRequest, opts ...grpc.CallOption) (*pb.TopRoutesResponse, error) { - return c.TopRoutesResponseToReturn, c.ErrorToReturn -} - -// Edges provides a mock of a Public API method. -func (c *MockAPIClient) Edges(ctx context.Context, in *pb.EdgesRequest, opts ...grpc.CallOption) (*pb.EdgesResponse, error) { - return c.EdgesResponseToReturn, c.ErrorToReturn + ErrorToReturn error + VersionInfoToReturn *publicPb.VersionInfo + DestinationGetClientToReturn destinationPb.Destination_GetClient } // Version provides a mock of a Public API method. @@ -62,26 +27,6 @@ func (c *MockAPIClient) Version(ctx context.Context, in *publicPb.Empty, opts .. return c.VersionInfoToReturn, c.ErrorToReturn } -// ListPods provides a mock of a Public API method. -func (c *MockAPIClient) ListPods(ctx context.Context, in *pb.ListPodsRequest, opts ...grpc.CallOption) (*pb.ListPodsResponse, error) { - return c.ListPodsResponseToReturn, c.ErrorToReturn -} - -// ListServices provides a mock of a Public API method. -func (c *MockAPIClient) ListServices(ctx context.Context, in *pb.ListServicesRequest, opts ...grpc.CallOption) (*pb.ListServicesResponse, error) { - return c.ListServicesResponseToReturn, c.ErrorToReturn -} - -// Tap provides a mock of a Public API method. -func (c *MockAPIClient) Tap(ctx context.Context, in *pb.TapRequest, opts ...grpc.CallOption) (pb.Api_TapClient, error) { - return c.APITapClientToReturn, c.ErrorToReturn -} - -// TapByResource provides a mock of a Public API method. -func (c *MockAPIClient) TapByResource(ctx context.Context, in *pb.TapByResourceRequest, opts ...grpc.CallOption) (pb.Api_TapByResourceClient, error) { - return c.APITapByResourceClientToReturn, c.ErrorToReturn -} - // Get provides a mock of a Public API method. func (c *MockAPIClient) Get(ctx context.Context, in *destinationPb.GetDestination, opts ...grpc.CallOption) (destinationPb.Destination_GetClient, error) { return c.DestinationGetClientToReturn, c.ErrorToReturn @@ -93,11 +38,6 @@ func (c *MockAPIClient) GetProfile(ctx context.Context, _ *destinationPb.GetDest return nil, errors.New("Not implemented") } -// SelfCheck provides a mock of a Public API method. -func (c *MockAPIClient) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) { - return c.SelfCheckResponseToReturn, c.ErrorToReturn -} - // MockDestinationGetClient satisfies the Destination_GetClient gRPC interface. type MockDestinationGetClient struct { UpdatesToReturn []destinationPb.Update @@ -167,16 +107,6 @@ type MockProm struct { rwLock sync.Mutex } -// PodCounts is a test helper struct that is used for representing data in a -// StatTable.PodGroup.Row. -type PodCounts struct { - Status string - MeshedPods uint64 - RunningPods uint64 - FailedPods uint64 - Errors map[string]*pb.PodErrors -} - // Query performs a query for the given time. func (m *MockProm) Query(ctx context.Context, query string, ts time.Time) (model.Value, promv1.Warnings, error) { m.rwLock.Lock() @@ -272,334 +202,3 @@ func (m *MockProm) Rules(ctx context.Context) (promv1.RulesResult, error) { func (m *MockProm) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]promv1.MetricMetadata, error) { return []promv1.MetricMetadata{}, nil } - -// GenStatSummaryResponse generates a mock Public API StatSummaryResponse -// object. -func GenStatSummaryResponse(resName, resType string, resNs []string, counts *PodCounts, basicStats bool, tcpStats bool) *pb.StatSummaryResponse { - rows := []*pb.StatTable_PodGroup_Row{} - for _, ns := range resNs { - statTableRow := &pb.StatTable_PodGroup_Row{ - Resource: &pb.Resource{ - Namespace: ns, - Type: resType, - Name: resName, - }, - TimeWindow: "1m", - } - - if basicStats { - statTableRow.Stats = &pb.BasicStats{ - SuccessCount: 123, - FailureCount: 0, - LatencyMsP50: 123, - LatencyMsP95: 123, - LatencyMsP99: 123, - } - } - - if tcpStats { - statTableRow.TcpStats = &pb.TcpStats{ - OpenConnections: 123, - ReadBytesTotal: 123, - WriteBytesTotal: 123, - } - } - - if counts != nil { - statTableRow.MeshedPodCount = counts.MeshedPods - statTableRow.RunningPodCount = counts.RunningPods - statTableRow.FailedPodCount = counts.FailedPods - statTableRow.Status = counts.Status - statTableRow.ErrorsByPod = counts.Errors - } - - rows = append(rows, statTableRow) - } - - resp := &pb.StatSummaryResponse{ - Response: &pb.StatSummaryResponse_Ok_{ // /~https://github.com/golang/protobuf/issues/205 - Ok: &pb.StatSummaryResponse_Ok{ - StatTables: []*pb.StatTable{ - { - Table: &pb.StatTable_PodGroup_{ - PodGroup: &pb.StatTable_PodGroup{ - Rows: rows, - }, - }, - }, - }, - }, - }, - } - - return resp -} - -// GenStatTsResponse generates a mock Public API StatSummaryResponse -// object in response to a request for trafficsplit stats. -func GenStatTsResponse(resName, resType string, resNs []string, basicStats bool, tsStats bool) *pb.StatSummaryResponse { - leaves := map[string]string{ - "service-1": "900m", - "service-2": "100m", - } - apex := "apex_name" - - rows := []*pb.StatTable_PodGroup_Row{} - for _, ns := range resNs { - for name, weight := range leaves { - statTableRow := &pb.StatTable_PodGroup_Row{ - Resource: &pb.Resource{ - Namespace: ns, - Type: resType, - Name: resName, - }, - TimeWindow: "1m", - } - - if basicStats { - statTableRow.Stats = &pb.BasicStats{ - SuccessCount: 123, - FailureCount: 0, - LatencyMsP50: 123, - LatencyMsP95: 123, - LatencyMsP99: 123, - } - } - - if tsStats { - statTableRow.TsStats = &pb.TrafficSplitStats{ - Apex: apex, - Leaf: name, - Weight: weight, - } - } - rows = append(rows, statTableRow) - - } - } - - // sort rows before returning in order to have a consistent order for tests - rows = sortTrafficSplitRows(rows) - - resp := &pb.StatSummaryResponse{ - Response: &pb.StatSummaryResponse_Ok_{ // /~https://github.com/golang/protobuf/issues/205 - Ok: &pb.StatSummaryResponse_Ok{ - StatTables: []*pb.StatTable{ - { - Table: &pb.StatTable_PodGroup_{ - PodGroup: &pb.StatTable_PodGroup{ - Rows: rows, - }, - }, - }, - }, - }, - }, - } - return resp -} - -type mockEdgeRow struct { - resourceType string - src string - dst string - srcNamespace string - dstNamespace string - clientID string - serverID string - msg string -} - -// a slice of edge rows to generate mock results -var emojivotoEdgeRows = []*mockEdgeRow{ - { - resourceType: "deployment", - src: "web", - dst: "voting", - srcNamespace: "emojivoto", - dstNamespace: "emojivoto", - clientID: "web.emojivoto.serviceaccount.identity.linkerd.cluster.local", - serverID: "voting.emojivoto.serviceaccount.identity.linkerd.cluster.local", - msg: "", - }, - { - resourceType: "deployment", - src: "vote-bot", - dst: "web", - srcNamespace: "emojivoto", - dstNamespace: "emojivoto", - clientID: "default.emojivoto.serviceaccount.identity.linkerd.cluster.local", - serverID: "web.emojivoto.serviceaccount.identity.linkerd.cluster.local", - msg: "", - }, - { - resourceType: "deployment", - src: "web", - dst: "emoji", - srcNamespace: "emojivoto", - dstNamespace: "emojivoto", - clientID: "web.emojivoto.serviceaccount.identity.linkerd.cluster.local", - serverID: "emoji.emojivoto.serviceaccount.identity.linkerd.cluster.local", - msg: "", - }, -} - -// a slice of edge rows to generate mock results -var linkerdEdgeRows = []*mockEdgeRow{ - { - resourceType: "deployment", - src: "linkerd-controller", - dst: "linkerd-prometheus", - srcNamespace: "linkerd", - dstNamespace: "linkerd", - clientID: "linkerd-controller.linkerd.identity.linkerd.cluster.local", - serverID: "linkerd-prometheus.linkerd.identity.linkerd.cluster.local", - msg: "", - }, -} - -// GenEdgesResponse generates a mock Public API EdgesResponse -// object. -func GenEdgesResponse(resourceType string, edgeRowNamespace string) *pb.EdgesResponse { - edgeRows := emojivotoEdgeRows - - if edgeRowNamespace == "linkerd" { - edgeRows = linkerdEdgeRows - } else if edgeRowNamespace == "all" { - // combine emojivotoEdgeRows and linkerdEdgeRows - edgeRows = append(edgeRows, linkerdEdgeRows...) - } - - edges := []*pb.Edge{} - for _, row := range edgeRows { - edge := &pb.Edge{ - Src: &pb.Resource{ - Name: row.src, - Namespace: row.srcNamespace, - Type: row.resourceType, - }, - Dst: &pb.Resource{ - Name: row.dst, - Namespace: row.dstNamespace, - Type: row.resourceType, - }, - ClientId: row.clientID, - ServerId: row.serverID, - NoIdentityMsg: row.msg, - } - edges = append(edges, edge) - } - - // sorting to retain consistent order for tests - edges = sortEdgeRows(edges) - - resp := &pb.EdgesResponse{ - Response: &pb.EdgesResponse_Ok_{ - Ok: &pb.EdgesResponse_Ok{ - Edges: edges, - }, - }, - } - return resp -} - -// GenTopRoutesResponse generates a mock Public API TopRoutesResponse object. -func GenTopRoutesResponse(routes []string, counts []uint64, outbound bool, authority string) *pb.TopRoutesResponse { - rows := []*pb.RouteTable_Row{} - for i, route := range routes { - row := &pb.RouteTable_Row{ - Route: route, - Authority: authority, - Stats: &pb.BasicStats{ - SuccessCount: counts[i], - FailureCount: 0, - LatencyMsP50: 123, - LatencyMsP95: 123, - LatencyMsP99: 123, - }, - TimeWindow: "1m", - } - if outbound { - row.Stats.ActualSuccessCount = counts[i] - } - rows = append(rows, row) - } - defaultRow := &pb.RouteTable_Row{ - Route: "[DEFAULT]", - Authority: authority, - Stats: &pb.BasicStats{ - SuccessCount: counts[len(counts)-1], - FailureCount: 0, - LatencyMsP50: 123, - LatencyMsP95: 123, - LatencyMsP99: 123, - }, - TimeWindow: "1m", - } - if outbound { - defaultRow.Stats.ActualSuccessCount = counts[len(counts)-1] - } - rows = append(rows, defaultRow) - - resp := &pb.TopRoutesResponse{ - Response: &pb.TopRoutesResponse_Ok_{ - Ok: &pb.TopRoutesResponse_Ok{ - Routes: []*pb.RouteTable{ - { - Rows: rows, - Resource: "deploy/foobar", - }, - }, - }, - }, - } - - return resp -} - -type expectedStatRPC struct { - err error - k8sConfigs []string // k8s objects to seed the API - mockPromResponse model.Value // mock out a prometheus query response - expectedPrometheusQueries []string // queries we expect public-api to issue to prometheus -} - -func newMockGrpcServer(exp expectedStatRPC) (*MockProm, *grpcServer, error) { - k8sAPI, err := k8s.NewFakeAPI(exp.k8sConfigs...) - if err != nil { - return nil, nil, err - } - - mockProm := &MockProm{Res: exp.mockPromResponse} - fakeGrpcServer := newGrpcServer( - mockProm, - nil, - k8sAPI, - "linkerd", - "cluster.local", - []string{}, - ) - - k8sAPI.Sync(nil) - - return mockProm, fakeGrpcServer, nil -} - -func (exp expectedStatRPC) verifyPromQueries(mockProm *MockProm) error { - // if exp.expectedPrometheusQueries is an empty slice we still want to check no queries were executed. - if exp.expectedPrometheusQueries != nil { - sort.Strings(exp.expectedPrometheusQueries) - sort.Strings(mockProm.QueriesExecuted) - - // because reflect.DeepEqual([]string{}, nil) is false - if len(exp.expectedPrometheusQueries) == 0 && len(mockProm.QueriesExecuted) == 0 { - return nil - } - - if !reflect.DeepEqual(exp.expectedPrometheusQueries, mockProm.QueriesExecuted) { - return fmt.Errorf("Prometheus queries incorrect. \nExpected:\n%+v \nGot:\n%+v", - exp.expectedPrometheusQueries, mockProm.QueriesExecuted) - } - } - return nil -} diff --git a/controller/api/util/api_utils.go b/controller/api/util/api_utils.go index 61d1baabbbc02..e175f0f9158bd 100644 --- a/controller/api/util/api_utils.go +++ b/controller/api/util/api_utils.go @@ -5,13 +5,12 @@ import ( "errors" "fmt" "strings" - "time" + netPb "github.com/linkerd/linkerd2/controller/gen/common/net" "github.com/linkerd/linkerd2/pkg/k8s" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - corev1 "k8s.io/api/core/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -21,9 +20,6 @@ import ( */ var ( - defaultMetricTimeWindow = "1m" - metricTimeWindowLowerBound = time.Second * 15 //the window value needs to equal or larger than that - // ValidTargets specifies resource types allowed as a target: // target resource on an inbound query // target resource on an outbound 'to' query @@ -57,49 +53,6 @@ var ( } ) -// StatsBaseRequestParams contains parameters that are used to build requests -// for metrics data. This includes requests to StatSummary and TopRoutes. -type StatsBaseRequestParams struct { - TimeWindow string - Namespace string - ResourceType string - ResourceName string - AllNamespaces bool -} - -// StatsSummaryRequestParams contains parameters that are used to build -// StatSummary requests. -type StatsSummaryRequestParams struct { - StatsBaseRequestParams - ToNamespace string - ToType string - ToName string - FromNamespace string - FromType string - FromName string - SkipStats bool - TCPStats bool - LabelSelector string -} - -// EdgesRequestParams contains parameters that are used to build -// Edges requests. -type EdgesRequestParams struct { - Namespace string - ResourceType string - AllNamespaces bool -} - -// TopRoutesRequestParams contains parameters that are used to build TopRoutes -// requests. -type TopRoutesRequestParams struct { - StatsBaseRequestParams - ToNamespace string - ToType string - ToName string - LabelSelector string -} - // TapRequestParams contains parameters that are used to build a // TapByResourceRequest. type TapRequestParams struct { @@ -116,14 +69,6 @@ type TapRequestParams struct { LabelSelector string } -// GatewayRequestParams contains parameters that are used to build a -// GatewayRequest -type GatewayRequestParams struct { - RemoteClusterName string - GatewayNamespace string - TimeWindow string -} - // GRPCError generates a gRPC error code, as defined in // google.golang.org/grpc/status. // If the error is nil or already a gRPC error, return the error. @@ -156,211 +101,6 @@ func GRPCError(err error) error { return err } -// BuildStatSummaryRequest builds a Public API StatSummaryRequest from a -// StatsSummaryRequestParams. -func BuildStatSummaryRequest(p StatsSummaryRequestParams) (*pb.StatSummaryRequest, error) { - window := defaultMetricTimeWindow - if p.TimeWindow != "" { - w, err := time.ParseDuration(p.TimeWindow) - if err != nil { - return nil, err - } - - if w < metricTimeWindowLowerBound { - return nil, errors.New("metrics time window needs to be at least 15s") - } - - window = p.TimeWindow - } - - if p.AllNamespaces && p.ResourceName != "" { - return nil, errors.New("stats for a resource cannot be retrieved by name across all namespaces") - } - - targetNamespace := p.Namespace - if p.AllNamespaces { - targetNamespace = "" - } else if p.Namespace == "" { - targetNamespace = corev1.NamespaceDefault - } - - resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ResourceType) - if err != nil { - return nil, err - } - - statRequest := &pb.StatSummaryRequest{ - Selector: &pb.ResourceSelection{ - Resource: &pb.Resource{ - Namespace: targetNamespace, - Name: p.ResourceName, - Type: resourceType, - }, - LabelSelector: p.LabelSelector, - }, - TimeWindow: window, - SkipStats: p.SkipStats, - TcpStats: p.TCPStats, - } - - if p.ToName != "" || p.ToType != "" || p.ToNamespace != "" { - if p.ToNamespace == "" { - p.ToNamespace = targetNamespace - } - if p.ToType == "" { - p.ToType = resourceType - } - - toType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ToType) - if err != nil { - return nil, err - } - - toResource := pb.StatSummaryRequest_ToResource{ - ToResource: &pb.Resource{ - Namespace: p.ToNamespace, - Type: toType, - Name: p.ToName, - }, - } - statRequest.Outbound = &toResource - } - - if p.FromName != "" || p.FromType != "" || p.FromNamespace != "" { - if p.FromNamespace == "" { - p.FromNamespace = targetNamespace - } - if p.FromType == "" { - p.FromType = resourceType - } - - fromType, err := validateFromResourceType(p.FromType) - if err != nil { - return nil, err - } - - fromResource := pb.StatSummaryRequest_FromResource{ - FromResource: &pb.Resource{ - Namespace: p.FromNamespace, - Type: fromType, - Name: p.FromName, - }, - } - statRequest.Outbound = &fromResource - } - - return statRequest, nil -} - -// BuildEdgesRequest builds a Public API EdgesRequest from a -// EdgesRequestParams. -func BuildEdgesRequest(p EdgesRequestParams) (*pb.EdgesRequest, error) { - namespace := p.Namespace - - // If all namespaces was specified, ignore namespace value. - if p.AllNamespaces { - namespace = "" - } - - resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ResourceType) - if err != nil { - return nil, err - } - - edgesRequest := &pb.EdgesRequest{ - Selector: &pb.ResourceSelection{ - Resource: &pb.Resource{ - Namespace: namespace, - Type: resourceType, - }, - }, - } - - return edgesRequest, nil -} - -// BuildTopRoutesRequest builds a Public API TopRoutesRequest from a -// TopRoutesRequestParams. -func BuildTopRoutesRequest(p TopRoutesRequestParams) (*pb.TopRoutesRequest, error) { - window := defaultMetricTimeWindow - if p.TimeWindow != "" { - _, err := time.ParseDuration(p.TimeWindow) - if err != nil { - return nil, err - } - window = p.TimeWindow - } - - if p.AllNamespaces && p.ResourceName != "" { - return nil, errors.New("routes for a resource cannot be retrieved by name across all namespaces") - } - - targetNamespace := p.Namespace - if p.AllNamespaces { - targetNamespace = "" - } else if p.Namespace == "" { - targetNamespace = corev1.NamespaceDefault - } - - resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ResourceType) - if err != nil { - return nil, err - } - - topRoutesRequest := &pb.TopRoutesRequest{ - Selector: &pb.ResourceSelection{ - Resource: &pb.Resource{ - Namespace: targetNamespace, - Name: p.ResourceName, - Type: resourceType, - }, - LabelSelector: p.LabelSelector, - }, - TimeWindow: window, - } - - if p.ToName != "" || p.ToType != "" || p.ToNamespace != "" { - if p.ToNamespace == "" { - p.ToNamespace = targetNamespace - } - if p.ToType == "" { - p.ToType = resourceType - } - - toType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ToType) - if err != nil { - return nil, err - } - - toResource := pb.TopRoutesRequest_ToResource{ - ToResource: &pb.Resource{ - Namespace: p.ToNamespace, - Type: toType, - Name: p.ToName, - }, - } - topRoutesRequest.Outbound = &toResource - } else { - topRoutesRequest.Outbound = &pb.TopRoutesRequest_None{ - None: &pb.Empty{}, - } - } - - return topRoutesRequest, nil -} - -// An authority can only receive traffic, not send it, so it can't be a --from -func validateFromResourceType(resourceType string) (string, error) { - name, err := k8s.CanonicalResourceNameFromFriendlyName(resourceType) - if err != nil { - return "", err - } - if name == k8s.Authority { - return "", errors.New("cannot query traffic --from an authority") - } - return name, nil -} - // BuildResource parses input strings, typically from CLI flags, to build a // Resource object for use in the protobuf API. // It's the same as BuildResources but only admits one arg and only returns one resource @@ -571,17 +311,17 @@ func contains(list []string, s string) bool { func CreateTapEvent(eventHTTP *pb.TapEvent_Http, dstMeta map[string]string, proxyDirection pb.TapEvent_ProxyDirection) *pb.TapEvent { event := &pb.TapEvent{ ProxyDirection: proxyDirection, - Source: &pb.TcpAddress{ - Ip: &pb.IPAddress{ - Ip: &pb.IPAddress_Ipv4{ + Source: &netPb.TcpAddress{ + Ip: &netPb.IPAddress{ + Ip: &netPb.IPAddress_Ipv4{ Ipv4: uint32(1), }, }, }, - Destination: &pb.TcpAddress{ - Ip: &pb.IPAddress{ - Ip: &pb.IPAddress_Ipv6{ - Ipv6: &pb.IPv6{ + Destination: &netPb.TcpAddress{ + Ip: &netPb.IPAddress{ + Ip: &netPb.IPAddress_Ipv6{ + Ipv6: &netPb.IPv6{ // All nodes address: https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml First: binary.BigEndian.Uint64([]byte{0xff, 0x01, 0, 0, 0, 0, 0, 0}), Last: binary.BigEndian.Uint64([]byte{0, 0, 0, 0, 0, 0, 0, 0x01}), @@ -598,63 +338,3 @@ func CreateTapEvent(eventHTTP *pb.TapEvent_Http, dstMeta map[string]string, prox } return event } - -// K8sPodToPublicPod converts a Kubernetes Pod to a Public API Pod -func K8sPodToPublicPod(pod corev1.Pod, ownerKind string, ownerName string) *pb.Pod { - status := string(pod.Status.Phase) - if pod.DeletionTimestamp != nil { - status = "Terminating" - } - - if pod.Status.Reason == "Evicted" { - status = "Evicted" - } - - controllerComponent := pod.Labels[k8s.ControllerComponentLabel] - controllerNS := pod.Labels[k8s.ControllerNSLabel] - - proxyReady := false - for _, container := range pod.Status.ContainerStatuses { - if container.Name == k8s.ProxyContainerName { - proxyReady = container.Ready - } - } - - proxyVersion := "" - for _, container := range pod.Spec.Containers { - if container.Name == k8s.ProxyContainerName { - parts := strings.Split(container.Image, ":") - proxyVersion = parts[1] - } - } - - item := &pb.Pod{ - Name: pod.Namespace + "/" + pod.Name, - Status: status, - PodIP: pod.Status.PodIP, - ControllerNamespace: controllerNS, - ControlPlane: controllerComponent != "", - ProxyReady: proxyReady, - ProxyVersion: proxyVersion, - ResourceVersion: pod.ResourceVersion, - } - - namespacedOwnerName := pod.Namespace + "/" + ownerName - - switch ownerKind { - case k8s.Deployment: - item.Owner = &pb.Pod_Deployment{Deployment: namespacedOwnerName} - case k8s.DaemonSet: - item.Owner = &pb.Pod_DaemonSet{DaemonSet: namespacedOwnerName} - case k8s.Job: - item.Owner = &pb.Pod_Job{Job: namespacedOwnerName} - case k8s.ReplicaSet: - item.Owner = &pb.Pod_ReplicaSet{ReplicaSet: namespacedOwnerName} - case k8s.ReplicationController: - item.Owner = &pb.Pod_ReplicationController{ReplicationController: namespacedOwnerName} - case k8s.StatefulSet: - item.Owner = &pb.Pod_StatefulSet{StatefulSet: namespacedOwnerName} - } - - return item -} diff --git a/controller/api/util/api_utils_test.go b/controller/api/util/api_utils_test.go index e997c5d5bd031..4cf1de05f9d59 100644 --- a/controller/api/util/api_utils_test.go +++ b/controller/api/util/api_utils_test.go @@ -10,9 +10,7 @@ import ( pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - corev1 "k8s.io/api/core/v1" k8sError "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -40,157 +38,6 @@ func TestGRPCError(t *testing.T) { }) } -func TestBuildStatSummaryRequest(t *testing.T) { - t.Run("Maps Kubernetes friendly names to canonical names", func(t *testing.T) { - expectations := map[string]string{ - "deployments": k8s.Deployment, - "deployment": k8s.Deployment, - "deploy": k8s.Deployment, - "pods": k8s.Pod, - "pod": k8s.Pod, - "po": k8s.Pod, - } - - for friendly, canonical := range expectations { - statSummaryRequest, err := BuildStatSummaryRequest( - StatsSummaryRequestParams{ - StatsBaseRequestParams: StatsBaseRequestParams{ - ResourceType: friendly, - }, - }, - ) - if err != nil { - t.Fatalf("Unexpected error from BuildStatSummaryRequest [%s => %s]: %s", friendly, canonical, err) - } - if statSummaryRequest.Selector.Resource.Type != canonical { - t.Fatalf("Unexpected resource type from BuildStatSummaryRequest [%s => %s]: %s", friendly, canonical, statSummaryRequest.Selector.Resource.Type) - } - } - }) - - t.Run("Parses valid time windows", func(t *testing.T) { - expectations := []string{ - "1m", - "60s", - "1m", - } - - for _, timeWindow := range expectations { - statSummaryRequest, err := BuildStatSummaryRequest( - StatsSummaryRequestParams{ - StatsBaseRequestParams: StatsBaseRequestParams{ - TimeWindow: timeWindow, - ResourceType: k8s.Deployment, - }, - }, - ) - if err != nil { - t.Fatalf("Unexpected error from BuildStatSummaryRequest [%s => %s]", timeWindow, err) - } - if statSummaryRequest.TimeWindow != timeWindow { - t.Fatalf("Unexpected TimeWindow from BuildStatSummaryRequest [%s => %s]", timeWindow, statSummaryRequest.TimeWindow) - } - } - }) - - t.Run("Rejects invalid time windows", func(t *testing.T) { - expectations := map[string]string{ - "1": "time: missing unit in duration 1", - "s": "time: invalid duration s", - } - - for timeWindow, msg := range expectations { - _, err := BuildStatSummaryRequest( - StatsSummaryRequestParams{ - StatsBaseRequestParams: StatsBaseRequestParams{ - TimeWindow: timeWindow, - }, - }, - ) - if err == nil { - t.Fatalf("BuildStatSummaryRequest(%s) unexpectedly succeeded, should have returned %s", timeWindow, msg) - } - if err.Error() != msg { - t.Fatalf("BuildStatSummaryRequest(%s) should have returned: %s but got unexpected message: %s", timeWindow, msg, err) - } - } - }) - - t.Run("Rejects invalid Kubernetes resource types", func(t *testing.T) { - expectations := map[string]string{ - "foo": "cannot find Kubernetes canonical name from friendly name [foo]", - "": "cannot find Kubernetes canonical name from friendly name []", - } - - for input, msg := range expectations { - _, err := BuildStatSummaryRequest( - StatsSummaryRequestParams{ - StatsBaseRequestParams: StatsBaseRequestParams{ - ResourceType: input, - }, - }, - ) - if err == nil { - t.Fatalf("BuildStatSummaryRequest(%s) unexpectedly succeeded, should have returned %s", input, msg) - } - if err.Error() != msg { - t.Fatalf("BuildStatSummaryRequest(%s) should have returned: %s but got unexpected message: %s", input, msg, err) - } - } - }) -} - -func TestBuildTopRoutesRequest(t *testing.T) { - t.Run("Parses valid time windows", func(t *testing.T) { - expectations := []string{ - "1m", - "60s", - "1m", - } - - for _, timeWindow := range expectations { - topRoutesRequest, err := BuildTopRoutesRequest( - TopRoutesRequestParams{ - StatsBaseRequestParams: StatsBaseRequestParams{ - TimeWindow: timeWindow, - ResourceType: k8s.Deployment, - }, - }, - ) - if err != nil { - t.Fatalf("Unexpected error from BuildTopRoutesRequest [%s => %s]", timeWindow, err) - } - if topRoutesRequest.TimeWindow != timeWindow { - t.Fatalf("Unexpected TimeWindow from BuildTopRoutesRequest [%s => %s]", timeWindow, topRoutesRequest.TimeWindow) - } - } - }) - - t.Run("Rejects invalid time windows", func(t *testing.T) { - expectations := map[string]string{ - "1": "time: missing unit in duration 1", - "s": "time: invalid duration s", - } - - for timeWindow, msg := range expectations { - _, err := BuildTopRoutesRequest( - TopRoutesRequestParams{ - StatsBaseRequestParams: StatsBaseRequestParams{ - TimeWindow: timeWindow, - ResourceType: k8s.Deployment, - }, - }, - ) - if err == nil { - t.Fatalf("BuildTopRoutesRequest(%s) unexpectedly succeeded, should have returned %s", timeWindow, msg) - } - if err.Error() != msg { - t.Fatalf("BuildTopRoutesRequest(%s) should have returned: %s but got unexpected message: %s", timeWindow, msg, err) - } - } - }) -} - type resourceExp struct { namespace string args []string @@ -399,94 +246,3 @@ func TestBuildResources(t *testing.T) { } }) } - -func TestK8sPodToPublicPod(t *testing.T) { - type podExp struct { - k8sPod corev1.Pod - ownerKind string - ownerName string - publicPod *pb.Pod - } - - t.Run("Returns expected pods", func(t *testing.T) { - expectations := []podExp{ - { - k8sPod: corev1.Pod{}, - publicPod: &pb.Pod{ - Name: "/", - }, - }, - { - k8sPod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "name", - ResourceVersion: "resource-version", - Labels: map[string]string{ - k8s.ControllerComponentLabel: "controller-component", - k8s.ControllerNSLabel: "controller-ns", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: k8s.ProxyContainerName, - Image: "linkerd-proxy:test-version", - }, - }, - }, - Status: corev1.PodStatus{ - PodIP: "pod-ip", - Phase: "status", - ContainerStatuses: []corev1.ContainerStatus{ - { - Name: k8s.ProxyContainerName, - Ready: true, - }, - }, - }, - }, - ownerKind: k8s.Deployment, - ownerName: "owner-name", - publicPod: &pb.Pod{ - Name: "ns/name", - Owner: &pb.Pod_Deployment{Deployment: "ns/owner-name"}, - ResourceVersion: "resource-version", - ControlPlane: true, - ControllerNamespace: "controller-ns", - Status: "status", - ProxyReady: true, - ProxyVersion: "test-version", - PodIP: "pod-ip", - }, - }, - { - k8sPod: corev1.Pod{ - Status: corev1.PodStatus{ - Phase: "Failed", - Reason: "Evicted", - ContainerStatuses: []corev1.ContainerStatus{ - { - Name: k8s.ProxyContainerName, - Ready: true, - }, - }, - }, - }, - ownerName: "owner-name", - publicPod: &pb.Pod{ - Name: "/", - Status: "Evicted", - ProxyReady: true, - }, - }, - } - - for _, exp := range expectations { - res := K8sPodToPublicPod(exp.k8sPod, exp.ownerKind, exp.ownerName) - if !reflect.DeepEqual(exp.publicPod, res) { - t.Fatalf("Expected pod to be [%+v] but was [%+v]", exp.publicPod, res) - } - } - }) -} diff --git a/controller/cmd/public-api/main.go b/controller/cmd/public-api/main.go index e1bfd130f92fc..62a8692f57523 100644 --- a/controller/cmd/public-api/main.go +++ b/controller/cmd/public-api/main.go @@ -5,7 +5,6 @@ import ( "flag" "os" "os/signal" - "strings" "syscall" "github.com/linkerd/linkerd2/controller/api/destination" @@ -14,7 +13,6 @@ import ( "github.com/linkerd/linkerd2/pkg/admin" "github.com/linkerd/linkerd2/pkg/flags" "github.com/linkerd/linkerd2/pkg/trace" - promApi "github.com/prometheus/client_golang/api" log "github.com/sirupsen/logrus" ) @@ -24,11 +22,9 @@ func Main(args []string) { addr := cmd.String("addr", ":8085", "address to serve on") kubeConfigPath := cmd.String("kubeconfig", "", "path to kube config") - prometheusURL := cmd.String("prometheus-url", "", "prometheus url") metricsAddr := cmd.String("metrics-addr", ":9995", "address to serve scrapable metrics on") destinationAPIAddr := cmd.String("destination-addr", "127.0.0.1:8086", "address of destination service") controllerNamespace := cmd.String("controller-namespace", "linkerd", "namespace in which Linkerd is installed") - ignoredNamespaces := cmd.String("ignore-namespaces", "kube-system", "comma separated list of namespaces to not list pods from") clusterDomain := cmd.String("cluster-domain", "cluster.local", "kubernetes cluster domain") traceCollector := flags.AddTraceFlags(cmd) @@ -55,14 +51,6 @@ func Main(args []string) { log.Fatalf("Failed to initialize K8s API: %s", err) } - var prometheusClient promApi.Client - if *prometheusURL != "" { - prometheusClient, err = promApi.NewClient(promApi.Config{Address: *prometheusURL}) - if err != nil { - log.Fatal(err.Error()) - } - } - log.Info("Using cluster domain: ", *clusterDomain) if *traceCollector != "" { @@ -73,12 +61,10 @@ func Main(args []string) { server := public.NewServer( *addr, - prometheusClient, destinationClient, k8sAPI, *controllerNamespace, *clusterDomain, - strings.Split(*ignoredNamespaces, ","), ) k8sAPI.Sync(nil) // blocks until caches are synced diff --git a/viz/metrics-api/gen/viz/healthcheck/healthcheck.pb.go b/controller/gen/common/healthcheck/healthcheck.pb.go similarity index 56% rename from viz/metrics-api/gen/viz/healthcheck/healthcheck.pb.go rename to controller/gen/common/healthcheck/healthcheck.pb.go index c75853aa23c0a..3de1f21f097f4 100644 --- a/viz/metrics-api/gen/viz/healthcheck/healthcheck.pb.go +++ b/controller/gen/common/healthcheck/healthcheck.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.24.0 // protoc v3.6.0 -// source: healthcheck.proto +// source: common/healthcheck.proto package healthcheck @@ -58,11 +58,11 @@ func (x CheckStatus) String() string { } func (CheckStatus) Descriptor() protoreflect.EnumDescriptor { - return file_healthcheck_proto_enumTypes[0].Descriptor() + return file_common_healthcheck_proto_enumTypes[0].Descriptor() } func (CheckStatus) Type() protoreflect.EnumType { - return &file_healthcheck_proto_enumTypes[0] + return &file_common_healthcheck_proto_enumTypes[0] } func (x CheckStatus) Number() protoreflect.EnumNumber { @@ -71,7 +71,7 @@ func (x CheckStatus) Number() protoreflect.EnumNumber { // Deprecated: Use CheckStatus.Descriptor instead. func (CheckStatus) EnumDescriptor() ([]byte, []int) { - return file_healthcheck_proto_rawDescGZIP(), []int{0} + return file_common_healthcheck_proto_rawDescGZIP(), []int{0} } type CheckResult struct { @@ -81,14 +81,14 @@ type CheckResult struct { SubsystemName string `protobuf:"bytes,1,opt,name=SubsystemName,proto3" json:"SubsystemName,omitempty"` CheckDescription string `protobuf:"bytes,2,opt,name=CheckDescription,proto3" json:"CheckDescription,omitempty"` - Status CheckStatus `protobuf:"varint,3,opt,name=Status,proto3,enum=linkerd2.viz.healthcheck.CheckStatus" json:"Status,omitempty"` + Status CheckStatus `protobuf:"varint,3,opt,name=Status,proto3,enum=linkerd2.common.healthcheck.CheckStatus" json:"Status,omitempty"` FriendlyMessageToUser string `protobuf:"bytes,4,opt,name=FriendlyMessageToUser,proto3" json:"FriendlyMessageToUser,omitempty"` } func (x *CheckResult) Reset() { *x = CheckResult{} if protoimpl.UnsafeEnabled { - mi := &file_healthcheck_proto_msgTypes[0] + mi := &file_common_healthcheck_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -101,7 +101,7 @@ func (x *CheckResult) String() string { func (*CheckResult) ProtoMessage() {} func (x *CheckResult) ProtoReflect() protoreflect.Message { - mi := &file_healthcheck_proto_msgTypes[0] + mi := &file_common_healthcheck_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -114,7 +114,7 @@ func (x *CheckResult) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckResult.ProtoReflect.Descriptor instead. func (*CheckResult) Descriptor() ([]byte, []int) { - return file_healthcheck_proto_rawDescGZIP(), []int{0} + return file_common_healthcheck_proto_rawDescGZIP(), []int{0} } func (x *CheckResult) GetSubsystemName() string { @@ -154,7 +154,7 @@ type SelfCheckRequest struct { func (x *SelfCheckRequest) Reset() { *x = SelfCheckRequest{} if protoimpl.UnsafeEnabled { - mi := &file_healthcheck_proto_msgTypes[1] + mi := &file_common_healthcheck_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -167,7 +167,7 @@ func (x *SelfCheckRequest) String() string { func (*SelfCheckRequest) ProtoMessage() {} func (x *SelfCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_healthcheck_proto_msgTypes[1] + mi := &file_common_healthcheck_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -180,7 +180,7 @@ func (x *SelfCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SelfCheckRequest.ProtoReflect.Descriptor instead. func (*SelfCheckRequest) Descriptor() ([]byte, []int) { - return file_healthcheck_proto_rawDescGZIP(), []int{1} + return file_common_healthcheck_proto_rawDescGZIP(), []int{1} } type SelfCheckResponse struct { @@ -194,7 +194,7 @@ type SelfCheckResponse struct { func (x *SelfCheckResponse) Reset() { *x = SelfCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_healthcheck_proto_msgTypes[2] + mi := &file_common_healthcheck_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -207,7 +207,7 @@ func (x *SelfCheckResponse) String() string { func (*SelfCheckResponse) ProtoMessage() {} func (x *SelfCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_healthcheck_proto_msgTypes[2] + mi := &file_common_healthcheck_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -220,7 +220,7 @@ func (x *SelfCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SelfCheckResponse.ProtoReflect.Descriptor instead. func (*SelfCheckResponse) Descriptor() ([]byte, []int) { - return file_healthcheck_proto_rawDescGZIP(), []int{2} + return file_common_healthcheck_proto_rawDescGZIP(), []int{2} } func (x *SelfCheckResponse) GetResults() []*CheckResult { @@ -230,65 +230,66 @@ func (x *SelfCheckResponse) GetResults() []*CheckResult { return nil } -var File_healthcheck_proto protoreflect.FileDescriptor - -var file_healthcheck_proto_rawDesc = []byte{ - 0x0a, 0x11, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xd4, 0x01, - 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, - 0x0d, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x3d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x25, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x68, - 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x34, - 0x0a, 0x15, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x46, +var File_common_healthcheck_proto protoreflect.FileDescriptor + +var file_common_healthcheck_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xd7, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, + 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x6f, - 0x55, 0x73, 0x65, 0x72, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x54, 0x0a, 0x11, 0x53, 0x65, 0x6c, 0x66, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, - 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, - 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x68, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x46, 0x72, 0x69, 0x65, + 0x6e, 0x64, 0x6c, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65, + 0x72, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x11, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, + 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2a, 0x2a, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, - 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, + 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2f, 0x76, 0x69, 0x7a, 0x2f, 0x6d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x76, 0x69, - 0x7a, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, + 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( - file_healthcheck_proto_rawDescOnce sync.Once - file_healthcheck_proto_rawDescData = file_healthcheck_proto_rawDesc + file_common_healthcheck_proto_rawDescOnce sync.Once + file_common_healthcheck_proto_rawDescData = file_common_healthcheck_proto_rawDesc ) -func file_healthcheck_proto_rawDescGZIP() []byte { - file_healthcheck_proto_rawDescOnce.Do(func() { - file_healthcheck_proto_rawDescData = protoimpl.X.CompressGZIP(file_healthcheck_proto_rawDescData) +func file_common_healthcheck_proto_rawDescGZIP() []byte { + file_common_healthcheck_proto_rawDescOnce.Do(func() { + file_common_healthcheck_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_healthcheck_proto_rawDescData) }) - return file_healthcheck_proto_rawDescData + return file_common_healthcheck_proto_rawDescData } -var file_healthcheck_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_healthcheck_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_healthcheck_proto_goTypes = []interface{}{ - (CheckStatus)(0), // 0: linkerd2.viz.healthcheck.CheckStatus - (*CheckResult)(nil), // 1: linkerd2.viz.healthcheck.CheckResult - (*SelfCheckRequest)(nil), // 2: linkerd2.viz.healthcheck.SelfCheckRequest - (*SelfCheckResponse)(nil), // 3: linkerd2.viz.healthcheck.SelfCheckResponse +var file_common_healthcheck_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_common_healthcheck_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_common_healthcheck_proto_goTypes = []interface{}{ + (CheckStatus)(0), // 0: linkerd2.common.healthcheck.CheckStatus + (*CheckResult)(nil), // 1: linkerd2.common.healthcheck.CheckResult + (*SelfCheckRequest)(nil), // 2: linkerd2.common.healthcheck.SelfCheckRequest + (*SelfCheckResponse)(nil), // 3: linkerd2.common.healthcheck.SelfCheckResponse } -var file_healthcheck_proto_depIdxs = []int32{ - 0, // 0: linkerd2.viz.healthcheck.CheckResult.Status:type_name -> linkerd2.viz.healthcheck.CheckStatus - 1, // 1: linkerd2.viz.healthcheck.SelfCheckResponse.results:type_name -> linkerd2.viz.healthcheck.CheckResult +var file_common_healthcheck_proto_depIdxs = []int32{ + 0, // 0: linkerd2.common.healthcheck.CheckResult.Status:type_name -> linkerd2.common.healthcheck.CheckStatus + 1, // 1: linkerd2.common.healthcheck.SelfCheckResponse.results:type_name -> linkerd2.common.healthcheck.CheckResult 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 @@ -296,13 +297,13 @@ var file_healthcheck_proto_depIdxs = []int32{ 0, // [0:2] is the sub-list for field type_name } -func init() { file_healthcheck_proto_init() } -func file_healthcheck_proto_init() { - if File_healthcheck_proto != nil { +func init() { file_common_healthcheck_proto_init() } +func file_common_healthcheck_proto_init() { + if File_common_healthcheck_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_healthcheck_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_common_healthcheck_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CheckResult); i { case 0: return &v.state @@ -314,7 +315,7 @@ func file_healthcheck_proto_init() { return nil } } - file_healthcheck_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_common_healthcheck_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SelfCheckRequest); i { case 0: return &v.state @@ -326,7 +327,7 @@ func file_healthcheck_proto_init() { return nil } } - file_healthcheck_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_common_healthcheck_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SelfCheckResponse); i { case 0: return &v.state @@ -343,19 +344,19 @@ func file_healthcheck_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_healthcheck_proto_rawDesc, + RawDescriptor: file_common_healthcheck_proto_rawDesc, NumEnums: 1, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_healthcheck_proto_goTypes, - DependencyIndexes: file_healthcheck_proto_depIdxs, - EnumInfos: file_healthcheck_proto_enumTypes, - MessageInfos: file_healthcheck_proto_msgTypes, + GoTypes: file_common_healthcheck_proto_goTypes, + DependencyIndexes: file_common_healthcheck_proto_depIdxs, + EnumInfos: file_common_healthcheck_proto_enumTypes, + MessageInfos: file_common_healthcheck_proto_msgTypes, }.Build() - File_healthcheck_proto = out.File - file_healthcheck_proto_rawDesc = nil - file_healthcheck_proto_goTypes = nil - file_healthcheck_proto_depIdxs = nil + File_common_healthcheck_proto = out.File + file_common_healthcheck_proto_rawDesc = nil + file_common_healthcheck_proto_goTypes = nil + file_common_healthcheck_proto_depIdxs = nil } diff --git a/controller/gen/common/net/net.pb.go b/controller/gen/common/net/net.pb.go new file mode 100644 index 0000000000000..028901ac07fd8 --- /dev/null +++ b/controller/gen/common/net/net.pb.go @@ -0,0 +1,337 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.24.0 +// protoc v3.6.0 +// source: common/net.proto + +package net + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +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) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type IPAddress struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Ip: + // *IPAddress_Ipv4 + // *IPAddress_Ipv6 + Ip isIPAddress_Ip `protobuf_oneof:"ip"` +} + +func (x *IPAddress) Reset() { + *x = IPAddress{} + if protoimpl.UnsafeEnabled { + mi := &file_common_net_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IPAddress) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IPAddress) ProtoMessage() {} + +func (x *IPAddress) ProtoReflect() protoreflect.Message { + mi := &file_common_net_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 IPAddress.ProtoReflect.Descriptor instead. +func (*IPAddress) Descriptor() ([]byte, []int) { + return file_common_net_proto_rawDescGZIP(), []int{0} +} + +func (m *IPAddress) GetIp() isIPAddress_Ip { + if m != nil { + return m.Ip + } + return nil +} + +func (x *IPAddress) GetIpv4() uint32 { + if x, ok := x.GetIp().(*IPAddress_Ipv4); ok { + return x.Ipv4 + } + return 0 +} + +func (x *IPAddress) GetIpv6() *IPv6 { + if x, ok := x.GetIp().(*IPAddress_Ipv6); ok { + return x.Ipv6 + } + return nil +} + +type isIPAddress_Ip interface { + isIPAddress_Ip() +} + +type IPAddress_Ipv4 struct { + Ipv4 uint32 `protobuf:"fixed32,1,opt,name=ipv4,proto3,oneof"` +} + +type IPAddress_Ipv6 struct { + Ipv6 *IPv6 `protobuf:"bytes,2,opt,name=ipv6,proto3,oneof"` +} + +func (*IPAddress_Ipv4) isIPAddress_Ip() {} + +func (*IPAddress_Ipv6) isIPAddress_Ip() {} + +type IPv6 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + First uint64 `protobuf:"fixed64,1,opt,name=first,proto3" json:"first,omitempty"` // hextets 1-4 + Last uint64 `protobuf:"fixed64,2,opt,name=last,proto3" json:"last,omitempty"` // hextets 5-8 +} + +func (x *IPv6) Reset() { + *x = IPv6{} + if protoimpl.UnsafeEnabled { + mi := &file_common_net_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IPv6) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IPv6) ProtoMessage() {} + +func (x *IPv6) ProtoReflect() protoreflect.Message { + mi := &file_common_net_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 IPv6.ProtoReflect.Descriptor instead. +func (*IPv6) Descriptor() ([]byte, []int) { + return file_common_net_proto_rawDescGZIP(), []int{1} +} + +func (x *IPv6) GetFirst() uint64 { + if x != nil { + return x.First + } + return 0 +} + +func (x *IPv6) GetLast() uint64 { + if x != nil { + return x.Last + } + return 0 +} + +type TcpAddress struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ip *IPAddress `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` +} + +func (x *TcpAddress) Reset() { + *x = TcpAddress{} + if protoimpl.UnsafeEnabled { + mi := &file_common_net_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TcpAddress) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TcpAddress) ProtoMessage() {} + +func (x *TcpAddress) ProtoReflect() protoreflect.Message { + mi := &file_common_net_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 TcpAddress.ProtoReflect.Descriptor instead. +func (*TcpAddress) Descriptor() ([]byte, []int) { + return file_common_net_proto_rawDescGZIP(), []int{2} +} + +func (x *TcpAddress) GetIp() *IPAddress { + if x != nil { + return x.Ip + } + return nil +} + +func (x *TcpAddress) GetPort() uint32 { + if x != nil { + return x.Port + } + return 0 +} + +var File_common_net_proto protoreflect.FileDescriptor + +var file_common_net_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x13, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x22, 0x58, 0x0a, 0x09, 0x49, 0x50, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x07, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x70, + 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, + 0x50, 0x76, 0x36, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x42, 0x04, 0x0a, 0x02, 0x69, + 0x70, 0x22, 0x30, 0x0a, 0x04, 0x49, 0x50, 0x76, 0x36, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x06, 0x52, 0x05, 0x66, 0x69, 0x72, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x06, 0x52, 0x04, 0x6c, + 0x61, 0x73, 0x74, 0x22, 0x50, 0x0a, 0x0a, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x2e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x02, 0x69, + 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_common_net_proto_rawDescOnce sync.Once + file_common_net_proto_rawDescData = file_common_net_proto_rawDesc +) + +func file_common_net_proto_rawDescGZIP() []byte { + file_common_net_proto_rawDescOnce.Do(func() { + file_common_net_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_net_proto_rawDescData) + }) + return file_common_net_proto_rawDescData +} + +var file_common_net_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_common_net_proto_goTypes = []interface{}{ + (*IPAddress)(nil), // 0: linkerd2.common.net.IPAddress + (*IPv6)(nil), // 1: linkerd2.common.net.IPv6 + (*TcpAddress)(nil), // 2: linkerd2.common.net.TcpAddress +} +var file_common_net_proto_depIdxs = []int32{ + 1, // 0: linkerd2.common.net.IPAddress.ipv6:type_name -> linkerd2.common.net.IPv6 + 0, // 1: linkerd2.common.net.TcpAddress.ip:type_name -> linkerd2.common.net.IPAddress + 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_common_net_proto_init() } +func file_common_net_proto_init() { + if File_common_net_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_common_net_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IPAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_net_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IPv6); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_common_net_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TcpAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_common_net_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*IPAddress_Ipv4)(nil), + (*IPAddress_Ipv6)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_common_net_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_common_net_proto_goTypes, + DependencyIndexes: file_common_net_proto_depIdxs, + MessageInfos: file_common_net_proto_msgTypes, + }.Build() + File_common_net_proto = out.File + file_common_net_proto_rawDesc = nil + file_common_net_proto_goTypes = nil + file_common_net_proto_depIdxs = nil +} diff --git a/controller/tap/server.go b/controller/tap/server.go index f94a08d55f88a..558506f751f64 100644 --- a/controller/tap/server.go +++ b/controller/tap/server.go @@ -12,6 +12,7 @@ import ( httpPb "github.com/linkerd/linkerd2-proxy-api/go/http_types" proxy "github.com/linkerd/linkerd2-proxy-api/go/tap" apiUtil "github.com/linkerd/linkerd2/controller/api/util" + netPb "github.com/linkerd/linkerd2/controller/gen/common/net" "github.com/linkerd/linkerd2/controller/k8s" "github.com/linkerd/linkerd2/pkg/addr" pkgK8s "github.com/linkerd/linkerd2/pkg/k8s" @@ -583,7 +584,7 @@ func (s *GRPCTapServer) hydrateEventLabels(ctx context.Context, ev *pb.TapEvent) // hydrateIPMeta attempts to determine the metadata labels for `ip` and, if // successful, adds them to `labels`. -func (s *GRPCTapServer) hydrateIPLabels(ctx context.Context, ip *pb.IPAddress, labels map[string]string) error { +func (s *GRPCTapServer) hydrateIPLabels(ctx context.Context, ip *netPb.IPAddress, labels map[string]string) error { res, err := s.resourceForIP(ip) if err != nil { return err @@ -613,7 +614,7 @@ func (s *GRPCTapServer) hydrateIPLabels(ctx context.Context, ip *pb.IPAddress, l // node if that's the case. Otherwise it checks the running pods that match the // IP. If exactly one is found, it's returned. Otherwise it returns nil. Errors // are returned only in the event of an error searching the indices. -func (s *GRPCTapServer) resourceForIP(ip *pb.IPAddress) (runtime.Object, error) { +func (s *GRPCTapServer) resourceForIP(ip *netPb.IPAddress) (runtime.Object, error) { ipStr := addr.PublicIPToString(ip) nodes, err := s.k8sAPI.Node().Informer().GetIndexer().ByIndex(ipIndex, ipStr) diff --git a/multicluster/cmd/check.go b/multicluster/cmd/check.go index 53ab6176c8efe..f4cbe41b192bc 100644 --- a/multicluster/cmd/check.go +++ b/multicluster/cmd/check.go @@ -15,7 +15,9 @@ import ( "github.com/linkerd/linkerd2/pkg/multicluster" "github.com/linkerd/linkerd2/pkg/servicemirror" "github.com/linkerd/linkerd2/pkg/tls" - public "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + vizCmd "github.com/linkerd/linkerd2/viz/cmd" + "github.com/linkerd/linkerd2/viz/metrics-api/client" + vizPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -464,12 +466,23 @@ func (hc *healthChecker) checkIfGatewayMirrorsHaveEndpoints(ctx context.Context) errors = append(errors, fmt.Errorf("%s.%s mirrored from cluster [%s] has no endpoints", svc.Name, svc.Namespace, svc.Labels[k8s.RemoteClusterNameLabel])) continue } + + vizNs, err := hc.linkerdHC.KubeAPIClient().GetNamespaceWithExtensionLabel(ctx, vizCmd.ExtensionName) + if err != nil { + return &healthcheck.SkipError{Reason: "failed to fetch gateway metrics"} + } + // Check gateway liveness according to probes - req := public.GatewaysRequest{ + vizClient, err := client.NewExternalClient(ctx, vizNs.Name, hc.linkerdHC.KubeAPIClient()) + if err != nil { + errors = append(errors, fmt.Errorf("failed to initialize viz client: %s", err)) + break + } + req := vizPb.GatewaysRequest{ TimeWindow: "1m", RemoteClusterName: link.TargetClusterName, } - rsp, err := hc.linkerdHC.VizAPIClient().Gateways(ctx, &req) + rsp, err := vizClient.Gateways(ctx, &req) if err != nil { errors = append(errors, fmt.Errorf("failed to fetch gateway metrics for %s.%s: %s", svc.Name, svc.Namespace, err)) continue diff --git a/multicluster/cmd/gateways.go b/multicluster/cmd/gateways.go index bd685054cfd41..d18aed9d71589 100644 --- a/multicluster/cmd/gateways.go +++ b/multicluster/cmd/gateways.go @@ -6,8 +6,9 @@ import ( "io" "github.com/linkerd/linkerd2/cli/table" - "github.com/linkerd/linkerd2/controller/api/public" "github.com/linkerd/linkerd2/pkg/k8s" + vizCmd "github.com/linkerd/linkerd2/viz/cmd" + "github.com/linkerd/linkerd2/viz/metrics-api/client" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" @@ -41,7 +42,14 @@ func newGatewaysCommand() *cobra.Command { return err } - client, err := public.NewExternalClient(cmd.Context(), controlPlaneNamespace, k8sAPI) + ctx := cmd.Context() + + vizNs, err := k8sAPI.GetNamespaceWithExtensionLabel(ctx, vizCmd.ExtensionName) + if err != nil { + return fmt.Errorf("make sure the linkerd-viz extension is installed, using 'linkerd viz install' (%s)", err) + } + + client, err := client.NewExternalClient(ctx, vizNs.Name, k8sAPI) if err != nil { return err } diff --git a/multicluster/cmd/root.go b/multicluster/cmd/root.go index 3ea3ba5a7cc0e..24ed52dc8cb0b 100644 --- a/multicluster/cmd/root.go +++ b/multicluster/cmd/root.go @@ -8,6 +8,7 @@ import ( "github.com/linkerd/linkerd2/pkg/charts/linkerd2" "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -60,6 +61,14 @@ components on a cluster, manage credentials and link clusters together.`, # Extract mirroring cluster credentials from cluster A and install them on cluster B linkerd --context=cluster-a multicluster link --cluster-name=target | kubectl apply --context=cluster-b -f -`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if verbose { + log.SetLevel(log.DebugLevel) + } else { + log.SetLevel(log.PanicLevel) + } + return nil + }, } multiclusterCmd.PersistentFlags().StringVarP(&controlPlaneNamespace, "linkerd-namespace", "L", defaultLinkerdNamespace, "Namespace in which Linkerd is installed") diff --git a/pkg/addr/addr.go b/pkg/addr/addr.go index b2039d0bd9dfe..63dd3d6c7947a 100644 --- a/pkg/addr/addr.go +++ b/pkg/addr/addr.go @@ -8,7 +8,7 @@ import ( "strings" pb "github.com/linkerd/linkerd2-proxy-api/go/net" - vizPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + l5dNetPb "github.com/linkerd/linkerd2/controller/gen/common/net" ) // DefaultWeight is the default address weight sent by the Destination service @@ -20,7 +20,7 @@ const DefaultWeight = 1 // If Ipv6, the bytes should be ordered big-endian. When formatted as a // string, the IP address should be enclosed in square brackets followed by // the port. -func PublicAddressToString(addr *vizPb.TcpAddress) string { +func PublicAddressToString(addr *l5dNetPb.TcpAddress) string { var s string if addr.GetIp().GetIpv6() != nil { s = "[%s]:%d" @@ -31,7 +31,7 @@ func PublicAddressToString(addr *vizPb.TcpAddress) string { } // PublicIPToString formats a Viz API IPAddress as a string. -func PublicIPToString(ip *vizPb.IPAddress) string { +func PublicIPToString(ip *l5dNetPb.IPAddress) string { var b []byte if ip.GetIpv6() != nil { b = make([]byte, 16) @@ -93,17 +93,17 @@ func ParseProxyIPV4(ip string) (*pb.IPAddress, error) { } // PublicIPV4 encodes 4 octets as a Viz API IPAddress. -func PublicIPV4(a1, a2, a3, a4 uint8) *vizPb.IPAddress { +func PublicIPV4(a1, a2, a3, a4 uint8) *l5dNetPb.IPAddress { ip := (uint32(a1) << 24) | (uint32(a2) << 16) | (uint32(a3) << 8) | uint32(a4) - return &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv4{ + return &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv4{ Ipv4: ip, }, } } // ParsePublicIPV4 parses an IP Address string into a Viz API IPAddress. -func ParsePublicIPV4(ip string) (*vizPb.IPAddress, error) { +func ParsePublicIPV4(ip string) (*l5dNetPb.IPAddress, error) { segments := strings.Split(ip, ".") if len(segments) != 4 { return nil, fmt.Errorf("Invalid IP address: %s", ip) @@ -121,28 +121,28 @@ func ParsePublicIPV4(ip string) (*vizPb.IPAddress, error) { // NetToPublic converts a Proxy API TCPAddress to a Viz API // TCPAddress -func NetToPublic(net *pb.TcpAddress) *vizPb.TcpAddress { - var ip *vizPb.IPAddress +func NetToPublic(net *pb.TcpAddress) *l5dNetPb.TcpAddress { + var ip *l5dNetPb.IPAddress switch i := net.GetIp().GetIp().(type) { case *pb.IPAddress_Ipv6: - ip = &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv6{ - Ipv6: &vizPb.IPv6{ + ip = &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv6{ + Ipv6: &l5dNetPb.IPv6{ First: i.Ipv6.First, Last: i.Ipv6.Last, }, }, } case *pb.IPAddress_Ipv4: - ip = &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv4{ + ip = &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv4{ Ipv4: i.Ipv4, }, } } - return &vizPb.TcpAddress{ + return &l5dNetPb.TcpAddress{ Ip: ip, Port: net.GetPort(), } diff --git a/pkg/addr/addr_test.go b/pkg/addr/addr_test.go index 779ba0f134ac4..e08c477199960 100644 --- a/pkg/addr/addr_test.go +++ b/pkg/addr/addr_test.go @@ -7,20 +7,20 @@ import ( "github.com/golang/protobuf/proto" pb "github.com/linkerd/linkerd2-proxy-api/go/net" proxy "github.com/linkerd/linkerd2-proxy-api/go/net" - vizPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + l5dNetPb "github.com/linkerd/linkerd2/controller/gen/common/net" ) func TestPublicAddressToString(t *testing.T) { cases := []struct { name string - addr *vizPb.TcpAddress + addr *l5dNetPb.TcpAddress expected string }{ { name: "ipv4", - addr: &vizPb.TcpAddress{ - Ip: &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv4{ + addr: &l5dNetPb.TcpAddress{ + Ip: &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv4{ Ipv4: 3232235521, }, }, @@ -30,10 +30,10 @@ func TestPublicAddressToString(t *testing.T) { }, { name: "ipv6", - addr: &vizPb.TcpAddress{ - Ip: &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv6{ - Ipv6: &vizPb.IPv6{ + addr: &l5dNetPb.TcpAddress{ + Ip: &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv6{ + Ipv6: &l5dNetPb.IPv6{ First: 49320, Last: 1, }, @@ -144,21 +144,21 @@ func TestNetToPublic(t *testing.T) { type addrExp struct { proxyAddr *proxy.TcpAddress - publicAddress *vizPb.TcpAddress + publicAddress *l5dNetPb.TcpAddress } expectations := []addrExp{ { proxyAddr: &proxy.TcpAddress{}, - publicAddress: &vizPb.TcpAddress{}, + publicAddress: &l5dNetPb.TcpAddress{}, }, { proxyAddr: &proxy.TcpAddress{ Ip: &proxy.IPAddress{Ip: &proxy.IPAddress_Ipv4{Ipv4: 1}}, Port: 1234, }, - publicAddress: &vizPb.TcpAddress{ - Ip: &vizPb.IPAddress{Ip: &vizPb.IPAddress_Ipv4{Ipv4: 1}}, + publicAddress: &l5dNetPb.TcpAddress{ + Ip: &l5dNetPb.IPAddress{Ip: &l5dNetPb.IPAddress_Ipv4{Ipv4: 1}}, Port: 1234, }, }, @@ -174,10 +174,10 @@ func TestNetToPublic(t *testing.T) { }, Port: 1234, }, - publicAddress: &vizPb.TcpAddress{ - Ip: &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv6{ - Ipv6: &vizPb.IPv6{ + publicAddress: &l5dNetPb.TcpAddress{ + Ip: &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv6{ + Ipv6: &l5dNetPb.IPv6{ First: 2345, Last: 6789, }, @@ -243,7 +243,7 @@ func TestParseProxyIPV4(t *testing.T) { func TestParsePublicIPV4(t *testing.T) { var testCases = []struct { ip string - expAddr *vizPb.IPAddress + expAddr *l5dNetPb.IPAddress expErr bool }{ { @@ -258,8 +258,8 @@ func TestParsePublicIPV4(t *testing.T) { }, { ip: "10.10.10.11", - expAddr: &vizPb.IPAddress{ - Ip: &vizPb.IPAddress_Ipv4{Ipv4: 168430091}, + expAddr: &l5dNetPb.IPAddress{ + Ip: &l5dNetPb.IPAddress_Ipv4{Ipv4: 168430091}, }, expErr: false, }, diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index 8b332f51738c9..27ee9570d2d43 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -12,6 +12,7 @@ import ( "time" "github.com/linkerd/linkerd2/controller/api/public" + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" configPb "github.com/linkerd/linkerd2/controller/gen/config" l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2" "github.com/linkerd/linkerd2/pkg/config" @@ -20,8 +21,6 @@ import ( "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/tls" "github.com/linkerd/linkerd2/pkg/version" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" log "github.com/sirupsen/logrus" admissionRegistration "k8s.io/api/admissionregistration/v1beta1" appsv1 "k8s.io/api/apps/v1" @@ -396,7 +395,6 @@ func NewCategory(id CategoryID, checkers []Checker, enabled bool) *Category { // Options specifies configuration for a HealthChecker. type Options struct { ControlPlaneNamespace string - VizNamespace string CNINamespace string DataPlaneNamespace string KubeConfig string @@ -420,8 +418,7 @@ type HealthChecker struct { kubeAPI *k8s.KubernetesAPI kubeVersion *k8sVersion.Info controlPlanePods []corev1.Pod - publicAPIClient public.PublicAPIClient - apiClient public.VizAPIClient + apiClient public.Client latestVersions version.Channels serverVersion string linkerdConfig *l5dcharts.Values @@ -431,6 +428,11 @@ type HealthChecker struct { cniDaemonSet *appsv1.DaemonSet } +// Runner is implemented by any health-checkers that can be triggered with RunChecks() +type Runner interface { + RunChecks(observer CheckObserver) bool +} + // NewHealthChecker returns an initialized HealthChecker func NewHealthChecker(categoryIDs []CategoryID, options *Options) *HealthChecker { hc := &HealthChecker{ @@ -458,6 +460,11 @@ func (hc *HealthChecker) AppendCategories(categories ...Category) *HealthChecker return hc } +// GetCategories returns all the categories +func (hc *HealthChecker) GetCategories() []Category { + return hc.categories +} + // allCategories is the global, ordered list of all checkers, grouped by // category. This method is attached to the HealthChecker struct because the // checkers directly reference other members of the struct, such as kubeAPI, @@ -743,10 +750,8 @@ func (hc *HealthChecker) allCategories() []Category { fatal: true, check: func(ctx context.Context) (err error) { if hc.APIAddr != "" { - hc.publicAPIClient, err = public.NewInternalPublicClient(hc.ControlPlaneNamespace, hc.APIAddr) hc.apiClient, err = public.NewInternalClient(hc.ControlPlaneNamespace, hc.APIAddr) } else { - hc.publicAPIClient, err = public.NewExternalPublicClient(ctx, hc.ControlPlaneNamespace, hc.kubeAPI) hc.apiClient, err = public.NewExternalClient(ctx, hc.ControlPlaneNamespace, hc.kubeAPI) } return @@ -758,7 +763,7 @@ func (hc *HealthChecker) allCategories() []Category { retryDeadline: hc.RetryDeadline, fatal: true, check: func(ctx context.Context) (err error) { - hc.serverVersion, err = GetServerVersion(ctx, hc.publicAPIClient) + hc.serverVersion, err = GetServerVersion(ctx, hc.apiClient) return }, }, @@ -1193,19 +1198,6 @@ func (hc *HealthChecker) allCategories() []Category { return validateControlPlanePods(hc.controlPlanePods) }, }, - { - description: "control plane self-check", - hintAnchor: "l5d-api-control-api", - // to avoid confusing users with a prometheus readiness error, we only show - // "waiting for check to complete" while things converge. If after the timeout - // it still hasn't converged, we show the real error (a 503 usually). - surfaceErrorOnRetry: false, - fatal: true, - retryDeadline: hc.RetryDeadline, - checkRPC: func(ctx context.Context) (*healthcheckPb.SelfCheckResponse, error) { - return hc.apiClient.SelfCheck(ctx, &healthcheckPb.SelfCheckRequest{}) - }, - }, }, }, { @@ -1303,9 +1295,9 @@ func (hc *HealthChecker) allCategories() []Category { outdatedPods := []string{} for _, pod := range pods { - err = hc.latestVersions.Match(pod.ProxyVersion) - if err != nil { - outdatedPods = append(outdatedPods, fmt.Sprintf("\t* %s (%s)", pod.Name, pod.ProxyVersion)) + proxyVersion := k8s.GetProxyVersion(pod) + if err = hc.latestVersions.Match(proxyVersion); err != nil { + outdatedPods = append(outdatedPods, fmt.Sprintf("\t* %s (%s)", pod.Name, proxyVersion)) } } if len(outdatedPods) > 0 { @@ -1326,8 +1318,9 @@ func (hc *HealthChecker) allCategories() []Category { } for _, pod := range pods { - if pod.ProxyVersion != version.Version { - return fmt.Errorf("%s running %s but cli running %s", pod.Name, pod.ProxyVersion, version.Version) + proxyVersion := k8s.GetProxyVersion(pod) + if proxyVersion != version.Version { + return fmt.Errorf("%s running %s but cli running %s", pod.Name, proxyVersion, version.Version) } } return nil @@ -1629,15 +1622,13 @@ func (hc *HealthChecker) KubeAPIClient() *k8s.KubernetesAPI { // PublicAPIClient returns a fully configured public API client. This client is // only configured if the KubernetesAPIChecks and LinkerdAPIChecks are // configured and run first. -func (hc *HealthChecker) PublicAPIClient() public.PublicAPIClient { - return hc.publicAPIClient +func (hc *HealthChecker) PublicAPIClient() public.Client { + return hc.apiClient } -// VizAPIClient returns a fully configured Viz API client. This client is -// only configured if the KubernetesAPIChecks and LinkerdAPIChecks are -// configured and run first. -func (hc *HealthChecker) VizAPIClient() public.VizAPIClient { - return hc.apiClient +// LatestVersions returns the latest versions from Linkerd release channels +func (hc *HealthChecker) LatestVersions() version.Channels { + return hc.latestVersions } func (hc *HealthChecker) checkLinkerdConfigConfigMap(ctx context.Context) (string, *l5dcharts.Values, error) { @@ -2163,29 +2154,13 @@ func checkResources(resourceName string, objects []runtime.Object, expectedNames } // GetDataPlanePods returns all the pods with data plane -func (hc *HealthChecker) GetDataPlanePods(ctx context.Context) ([]*pb.Pod, error) { - req := &pb.ListPodsRequest{} - if hc.DataPlaneNamespace != "" { - req.Selector = &pb.ResourceSelection{ - Resource: &pb.Resource{ - Namespace: hc.DataPlaneNamespace, - }, - } - } - - resp, err := hc.apiClient.ListPods(ctx, req) +func (hc *HealthChecker) GetDataPlanePods(ctx context.Context) ([]corev1.Pod, error) { + selector := fmt.Sprintf("%s=%s", k8s.ControllerNSLabel, hc.ControlPlaneNamespace) + podList, err := hc.kubeAPI.CoreV1().Pods(hc.DataPlaneNamespace).List(ctx, metav1.ListOptions{LabelSelector: selector}) if err != nil { return nil, err } - - pods := make([]*pb.Pod, 0) - for _, pod := range resp.GetPods() { - if pod.ControllerNamespace == hc.ControlPlaneNamespace { - pods = append(pods, pod) - } - } - - return pods, nil + return podList.Items, nil } func (hc *HealthChecker) checkHAMetadataPresentOnKubeSystemNamespace(ctx context.Context) error { @@ -2495,7 +2470,7 @@ func checkContainerRunning(pods []corev1.Pod, container string) error { return nil } -func validateDataPlanePods(pods []*pb.Pod, targetNamespace string) error { +func validateDataPlanePods(pods []corev1.Pod, targetNamespace string) error { if len(pods) == 0 { msg := fmt.Sprintf("No \"%s\" containers found", k8s.ProxyContainerName) if targetNamespace != "" { @@ -2505,12 +2480,12 @@ func validateDataPlanePods(pods []*pb.Pod, targetNamespace string) error { } for _, pod := range pods { - if pod.Status != "Running" && pod.Status != "Evicted" { - return fmt.Errorf("The \"%s\" pod is not running", - pod.Name) + status := k8s.GetPodStatus(pod) + if status != "Running" && status != "Evicted" { + return fmt.Errorf("The \"%s\" pod is not running", pod.Name) } - if !pod.ProxyReady { + if !k8s.GetProxyReady(pod) { return fmt.Errorf("The \"%s\" container in the \"%s\" pod is not ready", k8s.ProxyContainerName, pod.Name) } @@ -2519,28 +2494,6 @@ func validateDataPlanePods(pods []*pb.Pod, targetNamespace string) error { return nil } -func validateDataPlanePodReporting(pods []*pb.Pod) error { - notInPrometheus := []string{} - - for _, p := range pods { - // the `Added` field indicates the pod was found in Prometheus - if !p.Added { - notInPrometheus = append(notInPrometheus, p.Name) - } - } - - errMsg := "" - if len(notInPrometheus) > 0 { - errMsg = fmt.Sprintf("Data plane metrics not found for %s.", strings.Join(notInPrometheus, ", ")) - } - - if errMsg != "" { - return fmt.Errorf(errMsg) - } - - return nil -} - func checkUnschedulablePods(pods []corev1.Pod) error { var errors []string for _, pod := range pods { diff --git a/pkg/healthcheck/healthcheck_output.go b/pkg/healthcheck/healthcheck_output.go index 2124e65d27154..7e560ea98ca56 100644 --- a/pkg/healthcheck/healthcheck_output.go +++ b/pkg/healthcheck/healthcheck_output.go @@ -29,14 +29,14 @@ var ( ) // RunChecks runs the checks that are part of hc -func RunChecks(wout io.Writer, werr io.Writer, hc *HealthChecker, output string) bool { +func RunChecks(wout io.Writer, werr io.Writer, hc Runner, output string) bool { if output == JSONOutput { return runChecksJSON(wout, werr, hc) } return runChecksTable(wout, hc) } -func runChecksTable(wout io.Writer, hc *HealthChecker) bool { +func runChecksTable(wout io.Writer, hc Runner) bool { var lastCategory CategoryID spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond) spin.Writer = wout @@ -119,7 +119,7 @@ const ( checkErr checkResult = "error" ) -func runChecksJSON(wout io.Writer, werr io.Writer, hc *HealthChecker) bool { +func runChecksJSON(wout io.Writer, werr io.Writer, hc Runner) bool { var categories []*checkCategory collectJSONOutput := func(result *CheckResult) { diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index a60729a29e90d..1c8156d62d6fa 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -12,7 +12,7 @@ import ( "time" "github.com/golang/protobuf/ptypes/duration" - "github.com/linkerd/linkerd2/controller/api/public" + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" configPb "github.com/linkerd/linkerd2/controller/gen/config" "github.com/linkerd/linkerd2/pkg/charts/linkerd2" "github.com/linkerd/linkerd2/pkg/identity" @@ -20,8 +20,6 @@ import ( "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/tls" "github.com/linkerd/linkerd2/testutil" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" - healthcheckPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" "google.golang.org/protobuf/proto" corev1 "k8s.io/api/core/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" @@ -116,61 +114,6 @@ func TestHealthChecker(t *testing.T) { enabled: true, } - passingRPCClient := public.MockAPIClient{ - SelfCheckResponseToReturn: &healthcheckPb.SelfCheckResponse{ - Results: []*healthcheckPb.CheckResult{ - { - SubsystemName: "rpc1", - CheckDescription: "rpc desc1", - Status: healthcheckPb.CheckStatus_OK, - }, - }, - }, - } - - passingRPCCheck := Category{ - id: "cat4", - checkers: []Checker{ - { - description: "desc4", - checkRPC: func(context.Context) (*healthcheckPb.SelfCheckResponse, error) { - return passingRPCClient.SelfCheck(context.Background(), - &healthcheckPb.SelfCheckRequest{}) - }, - retryDeadline: time.Time{}, - }, - }, - enabled: true, - } - - failingRPCClient := public.MockAPIClient{ - SelfCheckResponseToReturn: &healthcheckPb.SelfCheckResponse{ - Results: []*healthcheckPb.CheckResult{ - { - SubsystemName: "rpc2", - CheckDescription: "rpc desc2", - Status: healthcheckPb.CheckStatus_FAIL, - FriendlyMessageToUser: "rpc error", - }, - }, - }, - } - - failingRPCCheck := Category{ - id: "cat5", - checkers: []Checker{ - { - description: "desc5", - checkRPC: func(context.Context) (*healthcheckPb.SelfCheckResponse, error) { - return failingRPCClient.SelfCheck(context.Background(), - &healthcheckPb.SelfCheckRequest{}) - }, - retryDeadline: time.Time{}, - }, - }, - enabled: true, - } - fatalCheck := Category{ id: "cat6", checkers: []Checker{ @@ -223,17 +166,11 @@ func TestHealthChecker(t *testing.T) { hc.AppendCategories(passingCheck1) hc.AppendCategories(passingCheck2) hc.AppendCategories(failingCheck) - hc.AppendCategories(passingRPCCheck) - hc.AppendCategories(failingRPCCheck) expectedResults := []string{ "cat1 desc1", "cat2 desc2", "cat3 desc3: error", - "cat4 desc4", - "cat4 [rpc1] rpc desc1", - "cat5 desc5", - "cat5 [rpc2] rpc desc2: rpc error", } obs := newObserver() @@ -251,7 +188,6 @@ func TestHealthChecker(t *testing.T) { ) hc.AppendCategories(passingCheck1) hc.AppendCategories(passingCheck2) - hc.AppendCategories(passingRPCCheck) success := hc.RunChecks(nullObserver) @@ -276,22 +212,6 @@ func TestHealthChecker(t *testing.T) { } }) - t.Run("Is not successful if one RPC check fails", func(t *testing.T) { - hc := NewHealthChecker( - []CategoryID{}, - &Options{}, - ) - hc.AppendCategories(passingCheck1) - hc.AppendCategories(failingRPCCheck) - hc.AppendCategories(passingCheck2) - - success := hc.RunChecks(nullObserver) - - if success { - t.Fatalf("Expecting checks to not be successful, but got [%t]", success) - } - }) - t.Run("Does not run remaining check if fatal check fails", func(t *testing.T) { hc := NewHealthChecker( []CategoryID{}, @@ -1782,7 +1702,7 @@ func TestValidateDataPlaneNamespace(t *testing.T) { func TestValidateDataPlanePods(t *testing.T) { t.Run("Returns an error if no inject pods were found", func(t *testing.T) { - err := validateDataPlanePods([]*pb.Pod{}, "emojivoto") + err := validateDataPlanePods([]corev1.Pod{}, "emojivoto") if err == nil { t.Fatal("Expected error, got nothing") } @@ -1792,11 +1712,55 @@ func TestValidateDataPlanePods(t *testing.T) { }) t.Run("Returns an error if not all pods are running", func(t *testing.T) { - pods := []*pb.Pod{ - {Name: "emoji-d9c7866bb-7v74n", Status: "Running", ProxyReady: true}, - {Name: "vote-bot-644b8cb6b4-g8nlr", Status: "Running", ProxyReady: true}, - {Name: "voting-65b9fffd77-rlwsd", Status: "Failed", ProxyReady: false}, - {Name: "web-6cfbccc48-5g8px", Status: "Running", ProxyReady: true}, + pods := []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "emoji-d9c7866bb-7v74n"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "vote-bot-644b8cb6b4-g8nlr"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "voting-65b9fffd77-rlwsd"}, + Status: corev1.PodStatus{ + Phase: "Failed", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: false, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "web-6cfbccc48-5g8px"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, } err := validateDataPlanePods(pods, "emojivoto") @@ -1809,8 +1773,19 @@ func TestValidateDataPlanePods(t *testing.T) { }) t.Run("Does not return an error if the pod is Evicted", func(t *testing.T) { - pods := []*pb.Pod{ - {Name: "emoji-d9c7866bb-7v74n", Status: "Evicted", ProxyReady: true}, + pods := []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "emoji-d9c7866bb-7v74n"}, + Status: corev1.PodStatus{ + Phase: "Evicted", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, } err := validateDataPlanePods(pods, "emojivoto") @@ -1820,11 +1795,55 @@ func TestValidateDataPlanePods(t *testing.T) { }) t.Run("Returns an error if the proxy container is not ready", func(t *testing.T) { - pods := []*pb.Pod{ - {Name: "emoji-d9c7866bb-7v74n", Status: "Running", ProxyReady: true}, - {Name: "vote-bot-644b8cb6b4-g8nlr", Status: "Running", ProxyReady: false}, - {Name: "voting-65b9fffd77-rlwsd", Status: "Running", ProxyReady: true}, - {Name: "web-6cfbccc48-5g8px", Status: "Running", ProxyReady: true}, + pods := []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "emoji-d9c7866bb-7v74n"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "vote-bot-644b8cb6b4-g8nlr"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: false, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "voting-65b9fffd77-rlwsd"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: false, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "web-6cfbccc48-5g8px"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, } err := validateDataPlanePods(pods, "emojivoto") @@ -1837,11 +1856,55 @@ func TestValidateDataPlanePods(t *testing.T) { }) t.Run("Returns nil if all pods are running and all proxy containers are ready", func(t *testing.T) { - pods := []*pb.Pod{ - {Name: "emoji-d9c7866bb-7v74n", Status: "Running", ProxyReady: true}, - {Name: "vote-bot-644b8cb6b4-g8nlr", Status: "Running", ProxyReady: true}, - {Name: "voting-65b9fffd77-rlwsd", Status: "Running", ProxyReady: true}, - {Name: "web-6cfbccc48-5g8px", Status: "Running", ProxyReady: true}, + pods := []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "emoji-d9c7866bb-7v74n"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "vote-bot-644b8cb6b4-g8nlr"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "voting-65b9fffd77-rlwsd"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "web-6cfbccc48-5g8px"}, + Status: corev1.PodStatus{ + Phase: "Running", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, } err := validateDataPlanePods(pods, "emojivoto") @@ -1850,43 +1913,6 @@ func TestValidateDataPlanePods(t *testing.T) { } }) } - -func TestValidateDataPlanePodReporting(t *testing.T) { - t.Run("Returns success if no pods present", func(t *testing.T) { - err := validateDataPlanePodReporting([]*pb.Pod{}) - if err != nil { - t.Fatalf("Unexpected error message: %s", err.Error()) - } - }) - - t.Run("Returns success if all pods are added", func(t *testing.T) { - pods := []*pb.Pod{ - {Name: "ns1/test1", Added: true}, - {Name: "ns2/test2", Added: true}, - } - - err := validateDataPlanePodReporting(pods) - if err != nil { - t.Fatalf("Unexpected error message: %s", err.Error()) - } - }) - - t.Run("Returns an error if any of the pod was not added to Prometheus", func(t *testing.T) { - pods := []*pb.Pod{ - {Name: "ns1/test1", Added: true}, - {Name: "ns2/test2", Added: false}, - } - - err := validateDataPlanePodReporting(pods) - if err == nil { - t.Fatal("Expected error, got nothing") - } - if err.Error() != "Data plane metrics not found for ns2/test2." { - t.Fatalf("Unexpected error message: %s", err.Error()) - } - }) -} - func TestLinkerdPreInstallGlobalResourcesChecks(t *testing.T) { hc := NewHealthChecker( []CategoryID{LinkerdPreInstallGlobalResourcesChecks}, diff --git a/pkg/k8s/api.go b/pkg/k8s/api.go index c23781fab21da..f76d2b2ef99e4 100644 --- a/pkg/k8s/api.go +++ b/pkg/k8s/api.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "strings" "time" "github.com/linkerd/linkerd2/pkg/prometheus" @@ -244,6 +245,27 @@ func GetPodStatus(pod corev1.Pod) string { return reason } +// GetProxyReady returns true if the pod contains a proxy that is ready +func GetProxyReady(pod corev1.Pod) bool { + for _, container := range pod.Status.ContainerStatuses { + if container.Name == ProxyContainerName { + return container.Ready + } + } + return false +} + +// GetProxyVersion returns the container proxy's version, if any +func GetProxyVersion(pod corev1.Pod) string { + for _, container := range pod.Spec.Containers { + if container.Name == ProxyContainerName { + parts := strings.Split(container.Image, ":") + return parts[1] + } + } + return "" +} + // GetAddOnsConfigMap returns the data in the add-ons configmap func GetAddOnsConfigMap(ctx context.Context, kubeAPI kubernetes.Interface, namespace string) (map[string]string, error) { cm, err := kubeAPI.CoreV1().ConfigMaps(namespace).Get(ctx, AddOnsConfigMapName, metav1.GetOptions{}) diff --git a/pkg/public/api.go b/pkg/public/api.go index a9eb15173fef9..d88f64bf49a5f 100644 --- a/pkg/public/api.go +++ b/pkg/public/api.go @@ -7,23 +7,13 @@ import ( "time" "github.com/linkerd/linkerd2/controller/api/public" - publicPb "github.com/linkerd/linkerd2/controller/gen/public" + pb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" - pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" ) // RawPublicAPIClient creates a raw public API client with no validation. -func RawPublicAPIClient(ctx context.Context, kubeAPI *k8s.KubernetesAPI, controlPlaneNamespace string, apiAddr string) (publicPb.ApiClient, error) { - if apiAddr != "" { - return public.NewInternalPublicClient(controlPlaneNamespace, apiAddr) - } - - return public.NewExternalPublicClient(ctx, controlPlaneNamespace, kubeAPI) -} - -// RawVizAPIClient creates a raw viz API client with no validation. -func RawVizAPIClient(ctx context.Context, kubeAPI *k8s.KubernetesAPI, controlPlaneNamespace string, apiAddr string) (pb.ApiClient, error) { +func RawPublicAPIClient(ctx context.Context, kubeAPI *k8s.KubernetesAPI, controlPlaneNamespace string, apiAddr string) (pb.ApiClient, error) { if apiAddr != "" { return public.NewInternalClient(controlPlaneNamespace, apiAddr) } @@ -34,24 +24,16 @@ func RawVizAPIClient(ctx context.Context, kubeAPI *k8s.KubernetesAPI, controlPla // CheckPublicAPIClientOrExit builds a new Public API client and executes default status // checks to determine if the client can successfully perform cli commands. If the // checks fail, then CLI will print an error and exit. -func CheckPublicAPIClientOrExit(hcOptions healthcheck.Options) public.PublicAPIClient { +func CheckPublicAPIClientOrExit(hcOptions healthcheck.Options) public.Client { hcOptions.RetryDeadline = time.Time{} return CheckPublicAPIClientOrRetryOrExit(hcOptions, false) } -// CheckVizAPIClientOrExit builds a new Viz API client and executes default status -// checks to determine if the client can successfully perform cli commands. If the -// checks fail, then CLI will print an error and exit. -func CheckVizAPIClientOrExit(hcOptions healthcheck.Options) public.VizAPIClient { - hcOptions.RetryDeadline = time.Time{} - return CheckVizAPIClientOrRetryOrExit(hcOptions, false) -} - // CheckPublicAPIClientOrRetryOrExit builds a new Public API client and executes status // checks to determine if the client can successfully connect to the API. If the // checks fail, then CLI will print an error and exit. If the hcOptions.retryDeadline // param is specified, then the CLI will print a message to stderr and retry. -func CheckPublicAPIClientOrRetryOrExit(hcOptions healthcheck.Options, apiChecks bool) public.PublicAPIClient { +func CheckPublicAPIClientOrRetryOrExit(hcOptions healthcheck.Options, apiChecks bool) public.Client { checks := []healthcheck.CategoryID{ healthcheck.KubernetesAPIChecks, healthcheck.LinkerdControlPlaneExistenceChecks, @@ -67,26 +49,6 @@ func CheckPublicAPIClientOrRetryOrExit(hcOptions healthcheck.Options, apiChecks return hc.PublicAPIClient() } -// CheckVizAPIClientOrRetryOrExit builds a new Viz API client and executes status -// checks to determine if the client can successfully connect to the API. If the -// checks fail, then CLI will print an error and exit. If the hcOptions.retryDeadline -// param is specified, then the CLI will print a message to stderr and retry. -func CheckVizAPIClientOrRetryOrExit(hcOptions healthcheck.Options, apiChecks bool) public.VizAPIClient { - checks := []healthcheck.CategoryID{ - healthcheck.KubernetesAPIChecks, - healthcheck.LinkerdControlPlaneExistenceChecks, - } - - if apiChecks { - checks = append(checks, healthcheck.LinkerdAPIChecks) - } - - hc := healthcheck.NewHealthChecker(checks, &hcOptions) - - hc.RunChecks(exitOnError) - return hc.VizAPIClient() -} - func exitOnError(result *healthcheck.CheckResult) { if result.Retry { fmt.Fprintln(os.Stderr, "Waiting for control plane to become available") diff --git a/viz/metrics-api/proto/healthcheck.proto b/proto/common/healthcheck.proto similarity index 73% rename from viz/metrics-api/proto/healthcheck.proto rename to proto/common/healthcheck.proto index 7feb9a4fe7cca..af5821e03341f 100644 --- a/viz/metrics-api/proto/healthcheck.proto +++ b/proto/common/healthcheck.proto @@ -1,8 +1,8 @@ syntax = "proto3"; -package linkerd2.viz.healthcheck; +package linkerd2.common.healthcheck; -option go_package = "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck"; +option go_package = "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"; enum CheckStatus { OK = 0; diff --git a/proto/common/net.proto b/proto/common/net.proto new file mode 100644 index 0000000000000..5a86d136b9ed2 --- /dev/null +++ b/proto/common/net.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package linkerd2.common.net; + +option go_package = "github.com/linkerd/linkerd2/controller/gen/common/net"; + +message IPAddress { + oneof ip { + fixed32 ipv4 = 1; + IPv6 ipv6 = 2; + } +} + +message IPv6 { + fixed64 first = 1; // hextets 1-4 + fixed64 last = 2; // hextets 5-8 +} + +message TcpAddress { + IPAddress ip = 1; + uint32 port = 2; +} + diff --git a/test/integration/install_test.go b/test/integration/install_test.go index b432d0405e600..a2e601605933c 100644 --- a/test/integration/install_test.go +++ b/test/integration/install_test.go @@ -496,6 +496,7 @@ func TestInstallHelm(t *testing.T) { "--set", "dashboard.image.tag=" + TestHelper.GetVersion(), "--set", "grafana.image.tag=" + TestHelper.GetVersion(), "--set", "tap.image.tag=" + TestHelper.GetVersion(), + "--set", "metricsAPI.image.tag=" + TestHelper.GetVersion(), "--set", "tapInjector.image.tag=" + TestHelper.GetVersion(), } // Install Viz Extension Chart @@ -606,6 +607,7 @@ func TestUpgradeHelm(t *testing.T) { "--set", "dashboard.image.tag=" + TestHelper.GetVersion(), "--set", "grafana.image.tag=" + TestHelper.GetVersion(), "--set", "tap.image.tag=" + TestHelper.GetVersion(), + "--set", "metricsAPI.image.tag=" + TestHelper.GetVersion(), "--set", "tapInjector.image.tag=" + TestHelper.GetVersion(), "--wait", } diff --git a/test/integration/stat/stat_test.go b/test/integration/stat/stat_test.go index 836c9712d35a6..1c231daf26c8d 100644 --- a/test/integration/stat/stat_test.go +++ b/test/integration/stat/stat_test.go @@ -46,15 +46,15 @@ func TestCliStatForLinkerdNamespace(t *testing.T) { } prometheusPod := pods[0] - pods, err = TestHelper.GetPodNamesForDeployment(ctx, TestHelper.GetLinkerdNamespace(), "linkerd-controller") + pods, err = TestHelper.GetPodNamesForDeployment(ctx, TestHelper.GetVizNamespace(), "linkerd-metrics-api") if err != nil { - testutil.AnnotatedFatalf(t, "failed to get pods for controller", - "failed to get pods for controller: %s", err) + testutil.AnnotatedFatalf(t, "failed to get pods for metrics-api", + "failed to get pods for metrics-api: %s", err) } if len(pods) != 1 { - testutil.Fatalf(t, "expected 1 pod for controller, got %d", len(pods)) + testutil.Fatalf(t, "expected 1 pod for metrics-api, got %d", len(pods)) } - controllerPod := pods[0] + metricsPod := pods[0] prometheusAuthority := "linkerd-prometheus." + TestHelper.GetVizNamespace() + ".svc.cluster.local:9090" @@ -76,36 +76,37 @@ func TestCliStatForLinkerdNamespace(t *testing.T) { { args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ - "linkerd-grafana": "1/1", - "linkerd-prometheus": "1/1", - "linkerd-tap": "1/1", - "linkerd-web": "1/1", - "tap-injector": "1/1", + "linkerd-metrics-api": "1/1", + "linkerd-grafana": "1/1", + "linkerd-prometheus": "1/1", + "linkerd-tap": "1/1", + "linkerd-web": "1/1", + "tap-injector": "1/1", }, }, { - args: []string{"viz", "stat", fmt.Sprintf("po/%s", prometheusPod), "-n", TestHelper.GetVizNamespace(), "--from", fmt.Sprintf("po/%s", controllerPod), "--from-namespace", TestHelper.GetLinkerdNamespace()}, + args: []string{"viz", "stat", fmt.Sprintf("po/%s", prometheusPod), "-n", TestHelper.GetVizNamespace(), "--from", fmt.Sprintf("po/%s", metricsPod), "--from-namespace", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ prometheusPod: "1/1", }, status: "Running", }, { - args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetLinkerdNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", TestHelper.GetVizNamespace()}, + args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ - "linkerd-controller": "1/1", + "linkerd-metrics-api": "1/1", }, }, { - args: []string{"viz", "stat", "svc", "linkerd-prometheus", "-n", TestHelper.GetVizNamespace(), "--from", "deploy/linkerd-controller", "--from-namespace", TestHelper.GetLinkerdNamespace()}, + args: []string{"viz", "stat", "svc", "linkerd-prometheus", "-n", TestHelper.GetVizNamespace(), "--from", "deploy/linkerd-metrics-api", "--from-namespace", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ "linkerd-prometheus": "1/1", }, }, { - args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetLinkerdNamespace(), "--to", "svc/linkerd-prometheus", "--to-namespace", TestHelper.GetVizNamespace()}, + args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace(), "--to", "svc/linkerd-prometheus", "--to-namespace", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ - "linkerd-controller": "1/1", + "linkerd-metrics-api": "1/1", }, }, { @@ -117,18 +118,18 @@ func TestCliStatForLinkerdNamespace(t *testing.T) { { args: []string{"viz", "stat", "ns", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ - TestHelper.GetVizNamespace(): "5/5", + TestHelper.GetVizNamespace(): "6/6", }, }, { - args: []string{"viz", "stat", "po", "-n", TestHelper.GetLinkerdNamespace(), "--to", fmt.Sprintf("au/%s", prometheusAuthority), "--to-namespace", TestHelper.GetVizNamespace()}, + args: []string{"viz", "stat", "po", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("au/%s", prometheusAuthority), "--to-namespace", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ - controllerPod: "1/1", + metricsPod: "1/1", }, status: "Running", }, { - args: []string{"viz", "stat", "au", "-n", TestHelper.GetLinkerdNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", TestHelper.GetVizNamespace()}, + args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", TestHelper.GetVizNamespace()}, expectedRows: map[string]string{ prometheusAuthority: "-", }, diff --git a/test/integration/testdata/check.cni.golden b/test/integration/testdata/check.cni.golden index d57f3efa7297d..3ce7a52a15de5 100644 --- a/test/integration/testdata/check.cni.golden +++ b/test/integration/testdata/check.cni.golden @@ -62,8 +62,6 @@ linkerd-webhooks-and-apisvc-tls linkerd-api ----------- √ control plane pods are ready -√ control plane self-check -√ [kubernetes] control plane can talk to Kubernetes linkerd-version --------------- diff --git a/test/integration/testdata/check.cni.proxy.golden b/test/integration/testdata/check.cni.proxy.golden index 9df457879bff6..6b3ea8f1bee8f 100644 --- a/test/integration/testdata/check.cni.proxy.golden +++ b/test/integration/testdata/check.cni.proxy.golden @@ -66,8 +66,6 @@ linkerd-identity-data-plane linkerd-api ----------- √ control plane pods are ready -√ control plane self-check -√ [kubernetes] control plane can talk to Kubernetes linkerd-version --------------- diff --git a/test/integration/testdata/check.golden b/test/integration/testdata/check.golden index 770dda1730b2c..eb786650a502b 100644 --- a/test/integration/testdata/check.golden +++ b/test/integration/testdata/check.golden @@ -50,8 +50,6 @@ linkerd-webhooks-and-apisvc-tls linkerd-api ----------- √ control plane pods are ready -√ control plane self-check -√ [kubernetes] control plane can talk to Kubernetes linkerd-version --------------- diff --git a/test/integration/testdata/check.multicluster.proxy.golden b/test/integration/testdata/check.multicluster.proxy.golden index 4d788221e8e5a..9f57563fe2b70 100644 --- a/test/integration/testdata/check.multicluster.proxy.golden +++ b/test/integration/testdata/check.multicluster.proxy.golden @@ -54,21 +54,12 @@ linkerd-identity-data-plane linkerd-api ----------- √ control plane pods are ready -√ control plane self-check -√ [kubernetes] control plane can talk to Kubernetes linkerd-version --------------- √ can determine the latest version √ cli is up-to-date -linkerd-data-plane ------------------- -√ data plane namespace exists -√ data plane proxies are ready -√ data plane is up-to-date -√ data plane and cli versions match - linkerd-multicluster -------------------- √ Link CRD exists diff --git a/test/integration/testdata/check.proxy.golden b/test/integration/testdata/check.proxy.golden index 880cc9b32e33d..5e4be31284157 100644 --- a/test/integration/testdata/check.proxy.golden +++ b/test/integration/testdata/check.proxy.golden @@ -54,8 +54,6 @@ linkerd-identity-data-plane linkerd-api ----------- √ control plane pods are ready -√ control plane self-check -√ [kubernetes] control plane can talk to Kubernetes linkerd-version --------------- diff --git a/viz/charts/linkerd-viz/README.md b/viz/charts/linkerd-viz/README.md index d92a6cc09d4d6..fa099fcec1cc0 100644 --- a/viz/charts/linkerd-viz/README.md +++ b/viz/charts/linkerd-viz/README.md @@ -106,6 +106,18 @@ Kubernetes: `>=1.13.0-0` | jaegerUrl | string | `""` | url of external jaeger instance Set this to `jaeger.linkerd-jaeger.svc.` if you plan to use jaeger extension | | linkerdNamespace | string | `"linkerd"` | Namespace of the Linkerd core control-plane install | | linkerdVersion | string | `"linkerdVersionValue"` | control plane version. See Proxy section for proxy version | +| metricsAPI.UID | int | `2103` | | +| metricsAPI.image.name | string | `"metrics-api"` | Docker image name for the metrics-api component | +| metricsAPI.image.pullPolicy | string | `"Always"` | Pull policy for the metrics-api component | +| metricsAPI.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the metrics-api component | +| metricsAPI.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the metrics-api component | +| metricsAPI.logLevel | string | `"info"` | log level of the metrics-api component | +| metricsAPI.proxy | string | `nil` | | +| metricsAPI.replicas | int | `1` | number of replicas of the metrics-api component | +| metricsAPI.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the metrics-api container can use | +| metricsAPI.resources.cpu.request | string | `nil` | Amount of CPU units that the metrics-api container requests | +| metricsAPI.resources.memory.limit | string | `nil` | Maximum amount of memory that metrics-api container can use | +| metricsAPI.resources.memory.request | string | `nil` | Amount of memory that the metrics-api container requests | | namespace | string | `"linkerd-viz"` | Namespace in which the Linkerd Viz extension has to be installed | | nodeSelector | object | `{"beta.kubernetes.io/os":"linux"}` | NodeSelector section, See the [K8S documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) for more information | | prometheus.alertRelabelConfigs | string | `nil` | Alert relabeling is applied to alerts before they are sent to the Alertmanager. | @@ -114,7 +126,7 @@ Kubernetes: `>=1.13.0-0` | prometheus.enabled | bool | `true` | toggle field to enable or disable prometheus | | prometheus.globalConfig | object | `{"evaluation_interval":"10s","scrape_interval":"10s","scrape_timeout":"10s"}` | The global configuration specifies parameters that are valid in all other configuration contexts. | | prometheus.image.name | string | `"prometheus"` | Docker image name for the prometheus instance | -| prometheus.image.pullPolicy | string | `"Always"` | | +| prometheus.image.pullPolicy | string | `"Always"` | Pull policy for the prometheus instance | | prometheus.image.registry | string | `"prom"` | Docker registry for the prometheus instance | | prometheus.image.tag | string | `"v2.19.3"` | Docker image tag for the prometheus instance | | prometheus.proxy | string | `nil` | | diff --git a/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml b/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml new file mode 100644 index 0000000000000..fa0a884dd2342 --- /dev/null +++ b/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml @@ -0,0 +1,54 @@ +--- +### +### Metrics API RBAC +### +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Values.namespace}}-metrics-api + labels: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api +rules: +- apiGroups: ["extensions", "apps"] + resources: ["daemonsets", "deployments", "replicasets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["extensions", "batch"] + resources: ["cronjobs", "jobs"] + verbs: ["list" , "get", "watch"] +- apiGroups: [""] + resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"] + verbs: ["list", "get", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["split.smi-spec.io"] + resources: ["trafficsplits"] + verbs: ["list", "get", "watch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Values.namespace}}-metrics-api + labels: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-{{.Values.namespace}}-metrics-api +subjects: +- kind: ServiceAccount + name: linkerd-metrics-api + namespace: {{.Values.namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: {{.Values.namespace}} + labels: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} diff --git a/viz/charts/linkerd-viz/templates/metrics-api.yaml b/viz/charts/linkerd-viz/templates/metrics-api.yaml new file mode 100644 index 0000000000000..8f9a4dab11656 --- /dev/null +++ b/viz/charts/linkerd-viz/templates/metrics-api.yaml @@ -0,0 +1,102 @@ +--- +### +### Metrics API +### +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: {{.Values.namespace}} + labels: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api + annotations: + {{.Values.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.linkerdVersion) .Values.cliVersion}} +spec: + type: ClusterIP + selector: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api + ports: + - name: http + port: 8085 + targetPort: 8085 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{.Values.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.linkerdVersion) .Values.cliVersion}} + labels: + {{.Values.extensionAnnotation}}: linkerd-viz + app.kubernetes.io/name: metrics-api + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{default .Values.linkerdVersion .Values.controllerImageVersion}} + component: metrics-api + name: linkerd-metrics-api + namespace: {{.Values.namespace}} +spec: + replicas: {{.Values.metricsAPI.replicas}} + selector: + matchLabels: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api + template: + metadata: + annotations: + {{- if empty .Values.cliVersion }} + checksum/config: {{ include (print $.Template.BasePath "/metrics-api-rbac.yaml") . | sha256sum }} + {{- end }} + {{.Values.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.linkerdVersion) .Values.cliVersion}} + {{- with .Values.metricsAPI.proxy }} + {{- include "partials.proxy.config.annotations" .resources | nindent 8 }} + {{- end }} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + labels: + {{.Values.extensionAnnotation}}: linkerd-viz + component: metrics-api + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- if .Values.enablePodAntiAffinity -}} + {{- $local := dict "component" "metrics-api" "label" "component" -}} + {{- include "linkerd.pod-affinity" $local | nindent 6 -}} + {{- end }} + containers: + - args: + - -controller-namespace={{.Values.linkerdNamespace}} + - -log-level={{.Values.metricsAPI.logLevel}} + - -cluster-domain={{.Values.clusterDomain}} + {{- if .Values.prometheusUrl }} + - -prometheus-url={{.Values.prometheusUrl}} + {{- else }} + - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.{{.Values.clusterDomain}}:9090 + {{- end }} + image: {{.Values.metricsAPI.image.registry}}/{{.Values.metricsAPI.image.name}}:{{.Values.metricsAPI.image.tag}} + imagePullPolicy: {{.Values.metricsAPI.pullPolicy}} + livenessProbe: + httpGet: + path: /ping + port: 9995 + initialDelaySeconds: 10 + name: metrics-api + ports: + - containerPort: 8085 + name: http + - containerPort: 9995 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9995 + {{- if .Values.metricsAPI.resources -}} + {{- include "partials.resources" .Values.metricsAPI.resources | nindent 8 }} + {{- end }} + securityContext: + runAsUser: {{.Values.metricsAPI.UID}} + serviceAccountName: linkerd-metrics-api diff --git a/viz/charts/linkerd-viz/templates/web.yaml b/viz/charts/linkerd-viz/templates/web.yaml index 2253decc62bfe..3a11b9b73bb81 100644 --- a/viz/charts/linkerd-viz/templates/web.yaml +++ b/viz/charts/linkerd-viz/templates/web.yaml @@ -68,7 +68,8 @@ spec: {{- include "linkerd.node-selector" . | nindent 6 }} containers: - args: - - -api-addr=linkerd-controller-api.{{.Values.linkerdNamespace}}.svc.{{.Values.clusterDomain}}:8085 + - -linkerd-controller-api-addr=linkerd-controller-api.{{.Values.linkerdNamespace}}.svc.{{.Values.clusterDomain}}:8085 + - -linkerd-metrics-api-addr=linkerd-metrics-api.{{.Values.namespace}}.svc.{{.Values.clusterDomain}}:8085 - -cluster-domain={{.Values.clusterDomain}} {{- if .Values.grafanaUrl }} - -grafana-addr={{.Values.grafanaUrl}} @@ -79,6 +80,7 @@ spec: - -jaeger-addr={{.Values.jaegerUrl}} {{- end}} - -controller-namespace={{.Values.linkerdNamespace}} + - -viz-namespace={{.Values.namespace}} - -log-level={{.Values.dashboard.logLevel}} {{- if .Values.dashboard.enforcedHostRegexp }} - -enforced-host={{.Values.dashboard.enforcedHostRegexp}} diff --git a/viz/charts/linkerd-viz/values.yaml b/viz/charts/linkerd-viz/values.yaml index 6b5801306d563..7a4ac2d132ea4 100644 --- a/viz/charts/linkerd-viz/values.yaml +++ b/viz/charts/linkerd-viz/values.yaml @@ -56,6 +56,41 @@ prometheusUrl: "" # Set this to `jaeger.linkerd-jaeger.svc.` if you plan to use jaeger extension jaegerUrl: "" +# metrics API configuration +metricsAPI: + # -- number of replicas of the metrics-api component + replicas: 1 + # -- log level of the metrics-api component + logLevel: *log_level + image: + # -- Docker registry for the metrics-api component + registry: ghcr.io/linkerd + # -- Docker image name for the metrics-api component + name: metrics-api + # -- Docker image tag for the metrics-api component + tag: *linkerd_version + # -- Pull policy for the metrics-api component + pullPolicy: Always + + resources: + cpu: + # -- Maximum amount of CPU units that the metrics-api container can use + limit: + # -- Amount of CPU units that the metrics-api container requests + request: + memory: + # -- Maximum amount of memory that metrics-api container can use + limit: + # -- Amount of memory that the metrics-api container requests + request: + + proxy: + # -- If set, overrides default proxy resources for the proxy injected + # into the metrics-api component + # resources: + + UID: *uid + # tap configuration tap: # -- Number of tap component replicas @@ -239,7 +274,7 @@ prometheus: name: prometheus # -- Docker image tag for the prometheus instance tag: v2.19.3 - # == Pull policy for the prometheus instance + # -- Pull policy for the prometheus instance pullPolicy: Always # -- Command line options for Prometheus binary diff --git a/viz/cmd/check.go b/viz/cmd/check.go index 6650970eb305c..be24d3219f985 100644 --- a/viz/cmd/check.go +++ b/viz/cmd/check.go @@ -1,38 +1,14 @@ package cmd import ( - "context" - "crypto/x509" "fmt" "io" "os" "time" "github.com/linkerd/linkerd2/pkg/healthcheck" - "github.com/linkerd/linkerd2/pkg/k8s" - "github.com/linkerd/linkerd2/pkg/tls" + vizHealthCheck "github.com/linkerd/linkerd2/viz/pkg/healthcheck" "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - apiregistrationv1client "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1" -) - -const ( - - // vizExtensionName is the name of Linkerd Viz extension - vizExtensionName = "linkerd-viz" - - // linkerdVizExtensionCheck adds checks related to the Linkerd Viz etension - linkerdVizExtensionCheck healthcheck.CategoryID = vizExtensionName - - // linkerdTapAPIServiceName is the name of the tap api service - // This key is passed to checkApiService method to check whether - // the api service is available or not - linkerdTapAPIServiceName = "v1alpha1.tap.linkerd.io" - - tapOldTLSSecretName = "linkerd-tap-tls" - tapTLSSecretName = "linkerd-tap-k8s-tls" ) type checkOptions struct { @@ -40,131 +16,6 @@ type checkOptions struct { output string } -func vizCategory(hc *healthcheck.HealthChecker) *healthcheck.Category { - checkers := []healthcheck.Checker{} - checkers = append(checkers, - *healthcheck.NewChecker("linkerd-viz Namespace exists"). - WithHintAnchor("l5d-viz-ns-exists"). - Fatal(). - Warning(). - WithCheck(func(ctx context.Context) error { - // Get viz Extension Namespace - ns, err := getNamespaceOfExtension(vizExtensionName) - if err != nil { - return err - } - hc.VizNamespace = ns.Name - return nil - })) - - checkers = append(checkers, - *healthcheck.NewChecker("linkerd-viz ClusterRoles exist"). - WithHintAnchor("l5d-viz-cr-exists"). - Fatal(). - Warning(). - WithCheck(func(ctx context.Context) error { - return healthcheck.CheckClusterRoles(ctx, hc.KubeAPIClient(), true, []string{fmt.Sprintf("linkerd-%s-prometheus", hc.VizNamespace), fmt.Sprintf("linkerd-%s-tap", hc.VizNamespace)}, "") - })) - - checkers = append(checkers, - *healthcheck.NewChecker("linkerd-viz ClusterRoleBindings exist"). - WithHintAnchor("l5d-viz-crb-exists"). - Fatal(). - Warning(). - WithCheck(func(ctx context.Context) error { - return healthcheck.CheckClusterRoleBindings(ctx, hc.KubeAPIClient(), true, []string{fmt.Sprintf("linkerd-%s-prometheus", hc.VizNamespace), fmt.Sprintf("linkerd-%s-tap", hc.VizNamespace)}, "") - })) - - checkers = append(checkers, - *healthcheck.NewChecker("linkerd-viz ConfigMaps exist"). - WithHintAnchor("l5d-viz-cm-exists"). - Fatal(). - Warning(). - WithCheck(func(ctx context.Context) error { - return healthcheck.CheckConfigMaps(ctx, hc.KubeAPIClient(), hc.VizNamespace, true, []string{"linkerd-prometheus-config", "linkerd-grafana-config"}, "") - })) - - checkers = append(checkers, - *healthcheck.NewChecker("tap API server has valid cert"). - WithHintAnchor("l5d-tap-cert-valid"). - Fatal(). - WithCheck(func(ctx context.Context) error { - anchors, err := fetchTapCaBundle(ctx, hc.KubeAPIClient()) - if err != nil { - return err - } - cert, err := hc.FetchCredsFromSecret(ctx, hc.VizNamespace, tapTLSSecretName) - if kerrors.IsNotFound(err) { - cert, err = hc.FetchCredsFromOldSecret(ctx, hc.VizNamespace, tapOldTLSSecretName) - } - if err != nil { - return err - } - - identityName := fmt.Sprintf("linkerd-tap.%s.svc", hc.VizNamespace) - return hc.CheckCertAndAnchors(cert, anchors, identityName) - })) - - checkers = append(checkers, - *healthcheck.NewChecker("tap API server cert is valid for at least 60 days"). - WithHintAnchor("l5d-webhook-cert-not-expiring-soon"). - Warning(). - WithCheck(func(ctx context.Context) error { - cert, err := hc.FetchCredsFromSecret(ctx, hc.VizNamespace, tapTLSSecretName) - if kerrors.IsNotFound(err) { - cert, err = hc.FetchCredsFromOldSecret(ctx, hc.VizNamespace, tapOldTLSSecretName) - } - if err != nil { - return err - } - return hc.CheckCertAndAnchorsExpiringSoon(cert) - })) - - checkers = append(checkers, - *healthcheck.NewChecker("tap API service is running"). - WithHintAnchor("l5d-tap-api"). - Warning(). - WithCheck(func(ctx context.Context) error { - return hc.CheckAPIService(ctx, linkerdTapAPIServiceName) - })) - - checkers = append(checkers, - *healthcheck.NewChecker("viz extension pods are running"). - WithHintAnchor("l5d-viz-pods-running"). - Warning(). - WithRetryDeadline(hc.RetryDeadline). - SurfaceErrorOnRetry(). - WithCheck(func(ctx context.Context) error { - pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, hc.VizNamespace) - if err != nil { - return err - } - - // Check for relevant pods to be present - err = healthcheck.CheckForPods(pods, []string{"linkerd-grafana", "linkerd-prometheus", "linkerd-web", "linkerd-tap"}) - if err != nil { - return err - } - - return healthcheck.CheckPodsRunning(pods, "") - })) - - checkers = append(checkers, - *healthcheck.NewChecker("linkerd-viz pods are injected"). - WithHintAnchor("l5d-viz-pods-injection"). - Warning(). - WithCheck(func(ctx context.Context) error { - pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, hc.VizNamespace) - if err != nil { - return err - } - return healthcheck.CheckIfDataPlanePodsExist(pods) - })) - - // TODO: Add dataplane metrics in prometheus check - return healthcheck.NewCategory(linkerdVizExtensionCheck, checkers, true) -} - func newCheckOptions() *checkOptions { return &checkOptions{ wait: 300 * time.Second, @@ -214,10 +65,10 @@ func configureAndRunChecks(wout io.Writer, werr io.Writer, options *checkOptions checks := []healthcheck.CategoryID{ healthcheck.KubernetesAPIChecks, healthcheck.LinkerdControlPlaneExistenceChecks, - linkerdVizExtensionCheck, + vizHealthCheck.LinkerdVizExtensionCheck, } - hc := healthcheck.NewHealthChecker(checks, &healthcheck.Options{ + hc := vizHealthCheck.NewHealthChecker(checks, &healthcheck.Options{ ControlPlaneNamespace: controlPlaneNamespace, KubeConfig: kubeconfigPath, KubeContext: kubeContext, @@ -227,8 +78,6 @@ func configureAndRunChecks(wout io.Writer, werr io.Writer, options *checkOptions RetryDeadline: time.Now().Add(options.wait), }) - hc.AppendCategories(*vizCategory(hc)) - success := healthcheck.RunChecks(wout, werr, hc, options.output) if !success { @@ -237,40 +86,3 @@ func configureAndRunChecks(wout io.Writer, werr io.Writer, options *checkOptions return nil } - -func getNamespaceOfExtension(name string) (*corev1.Namespace, error) { - kubeAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0) - if err != nil { - return nil, err - } - - namespaces, err := kubeAPI.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{LabelSelector: k8s.LinkerdExtensionLabel}) - if err != nil { - return nil, err - } - - for _, ns := range namespaces.Items { - if ns.Labels[k8s.LinkerdExtensionLabel] == name { - return &ns, err - } - } - return nil, fmt.Errorf("could not find the linkerd-viz extension. It can be installed by running `linkerd viz install | kubectl apply -f -`") -} - -func fetchTapCaBundle(ctx context.Context, kubeAPI *k8s.KubernetesAPI) ([]*x509.Certificate, error) { - apiServiceClient, err := apiregistrationv1client.NewForConfig(kubeAPI.Config) - if err != nil { - return nil, err - } - - apiService, err := apiServiceClient.APIServices().Get(ctx, linkerdTapAPIServiceName, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - caBundle, err := tls.DecodePEMCertificates(string(apiService.Spec.CABundle)) - if err != nil { - return nil, err - } - return caBundle, nil -} diff --git a/viz/cmd/dashboard.go b/viz/cmd/dashboard.go index 4a4a32d2d318e..cf3cdb8853685 100644 --- a/viz/cmd/dashboard.go +++ b/viz/cmd/dashboard.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "fmt" "os" "os/signal" @@ -97,7 +98,7 @@ func NewCmdDashboard() *cobra.Command { return err } - vizNamespace, err := getVizNamespace(cmd.Context(), k8sAPI) + vizNs, err := k8sAPI.GetNamespaceWithExtensionLabel(context.Background(), ExtensionName) if err != nil { return err } @@ -109,7 +110,7 @@ func NewCmdDashboard() *cobra.Command { portforward, err := k8s.NewPortForward( cmd.Context(), k8sAPI, - vizNamespace, + vizNs.Name, webDeployment, options.host, options.port, diff --git a/viz/cmd/edges.go b/viz/cmd/edges.go index 209c4d21c71f7..9d01ea27dbe07 100644 --- a/viz/cmd/edges.go +++ b/viz/cmd/edges.go @@ -11,11 +11,12 @@ import ( "text/tabwriter" "github.com/fatih/color" - "github.com/linkerd/linkerd2/controller/api/util" + coreUtil "github.com/linkerd/linkerd2/controller/api/util" pkgcmd "github.com/linkerd/linkerd2/pkg/cmd" "github.com/linkerd/linkerd2/pkg/healthcheck" - api "github.com/linkerd/linkerd2/pkg/public" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + "github.com/linkerd/linkerd2/viz/metrics-api/util" + "github.com/linkerd/linkerd2/viz/pkg/api" "github.com/spf13/cobra" ) @@ -82,7 +83,7 @@ func NewCmdEdges() *cobra.Command { # Get all edges between pods in all namespaces. linkerd viz edges po --all-namespaces`, Args: cobra.ExactArgs(1), - ValidArgs: util.ValidTargets, + ValidArgs: coreUtil.ValidTargets, RunE: func(cmd *cobra.Command, args []string) error { if options.namespace == "" { options.namespace = pkgcmd.GetDefaultNamespace(kubeconfigPath, kubeContext) @@ -95,7 +96,7 @@ func NewCmdEdges() *cobra.Command { // The gRPC client is concurrency-safe, so we can reuse it in all the following goroutines // /~https://github.com/grpc/grpc-go/issues/682 - client := api.CheckVizAPIClientOrExit(healthcheck.Options{ + client := api.CheckClientOrExit(healthcheck.Options{ ControlPlaneNamespace: controlPlaneNamespace, KubeConfig: kubeconfigPath, Impersonate: impersonate, @@ -159,7 +160,7 @@ func validateEdgesRequestInputs(targets []*pb.Resource, options *edgesOptions) e } func buildEdgesRequests(resources []string, options *edgesOptions) ([]*pb.EdgesRequest, error) { - targets, err := util.BuildResources(options.namespace, resources) + targets, err := coreUtil.BuildResources(options.namespace, resources) if err != nil { return nil, err diff --git a/viz/cmd/edges_test.go b/viz/cmd/edges_test.go index f47c962d4b316..e9312b9ba73fb 100644 --- a/viz/cmd/edges_test.go +++ b/viz/cmd/edges_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/linkerd/linkerd2/controller/api/public" + api "github.com/linkerd/linkerd2/viz/metrics-api" ) type edgesParamsExp struct { @@ -99,8 +99,8 @@ func TestEdges(t *testing.T) { } func testEdgesCall(exp edgesParamsExp, t *testing.T) { - mockClient := &public.MockAPIClient{} - response := public.GenEdgesResponse(exp.resourceType, "all") + mockClient := &api.MockAPIClient{} + response := api.GenEdgesResponse(exp.resourceType, "all") mockClient.EdgesResponseToReturn = response diff --git a/viz/cmd/install.go b/viz/cmd/install.go index d508111acbac6..7eb4a70218a53 100644 --- a/viz/cmd/install.go +++ b/viz/cmd/install.go @@ -24,11 +24,13 @@ import ( var ( templatesVIz = []string{ "templates/namespace.yaml", + "templates/metrics-api-rbac.yaml", "templates/grafana-rbac.yaml", "templates/prometheus-rbac.yaml", "templates/tap-rbac.yaml", "templates/web-rbac.yaml", "templates/psp.yaml", + "templates/metrics-api.yaml", "templates/grafana.yaml", "templates/prometheus.yaml", "templates/tap.yaml", diff --git a/viz/cmd/root.go b/viz/cmd/root.go index d11fecce3fb9e..e3347d9f1002c 100644 --- a/viz/cmd/root.go +++ b/viz/cmd/root.go @@ -1,19 +1,18 @@ package cmd import ( - "context" - "errors" "fmt" "regexp" "github.com/fatih/color" - "github.com/linkerd/linkerd2/pkg/k8s" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( + // ExtensionName is the value that the viz extension resources should be labeled with + ExtensionName = "linkerd-viz" + vizChartName = "linkerd-viz" defaultLinkerdNamespace = "linkerd" maxRps = 100.0 @@ -24,7 +23,6 @@ const ( ) var ( - // special handling for Windows, on all other platforms these resolve to // os.Stdout and os.Stderr, thanks to /~https://github.com/mattn/go-colorable stdout = color.Output @@ -84,14 +82,3 @@ func NewCmdViz() *cobra.Command { return vizCmd } - -func getVizNamespace(ctx context.Context, k8sAPI *k8s.KubernetesAPI) (string, error) { - ns, err := k8sAPI.CoreV1().Namespaces().List(ctx, metav1.ListOptions{LabelSelector: "linkerd.io/extension=linkerd-viz"}) - if err != nil { - return "", err - } - if len(ns.Items) == 0 { - return "", errors.New("linkerd-viz extension not found") - } - return ns.Items[0].Name, nil -} diff --git a/viz/cmd/routes.go b/viz/cmd/routes.go index 81c1893b926dc..23f7ec303f903 100644 --- a/viz/cmd/routes.go +++ b/viz/cmd/routes.go @@ -11,12 +11,13 @@ import ( "text/tabwriter" "time" - "github.com/linkerd/linkerd2/controller/api/util" + coreUtil "github.com/linkerd/linkerd2/controller/api/util" pkgcmd "github.com/linkerd/linkerd2/pkg/cmd" "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" - api "github.com/linkerd/linkerd2/pkg/public" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + "github.com/linkerd/linkerd2/viz/metrics-api/util" + "github.com/linkerd/linkerd2/viz/pkg/api" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -62,7 +63,7 @@ This command will only display traffic which is sent to a service that has a Ser # Routes for calls from the traffic deployment to the webapp service in the test namespace. linkerd viz routes deploy/traffic -n test --to svc/webapp`, Args: cobra.ExactArgs(1), - ValidArgs: util.ValidTargets, + ValidArgs: coreUtil.ValidTargets, RunE: func(cmd *cobra.Command, args []string) error { if options.namespace == "" { options.namespace = pkgcmd.GetDefaultNamespace(kubeconfigPath, kubeContext) @@ -73,7 +74,7 @@ This command will only display traffic which is sent to a service that has a Ser } output, err := requestRouteStatsFromAPI( - api.CheckVizAPIClientOrExit(healthcheck.Options{ + api.CheckClientOrExit(healthcheck.Options{ ControlPlaneNamespace: controlPlaneNamespace, KubeConfig: kubeconfigPath, Impersonate: impersonate, @@ -353,7 +354,7 @@ func buildTopRoutesRequest(resource string, options *routesOptions) (*pb.TopRout return nil, err } - target, err := util.BuildResource(options.namespace, resource) + target, err := coreUtil.BuildResource(options.namespace, resource) if err != nil { return nil, err } @@ -374,7 +375,7 @@ func buildTopRoutesRequest(resource string, options *routesOptions) (*pb.TopRout if options.toNamespace == "" { options.toNamespace = options.namespace } - toRes, err := util.BuildResource(options.toNamespace, options.toResource) + toRes, err := coreUtil.BuildResource(options.toNamespace, options.toResource) if err != nil { return nil, err } diff --git a/viz/cmd/routes_test.go b/viz/cmd/routes_test.go index 6be49f65c4b40..082af9a358e4f 100644 --- a/viz/cmd/routes_test.go +++ b/viz/cmd/routes_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/linkerd/linkerd2/controller/api/public" + api "github.com/linkerd/linkerd2/viz/metrics-api" ) type routesParamsExp struct { @@ -48,9 +48,9 @@ func TestRoutes(t *testing.T) { } func testRoutesCall(exp routesParamsExp, t *testing.T) { - mockClient := &public.MockAPIClient{} + mockClient := &api.MockAPIClient{} - response := public.GenTopRoutesResponse(exp.routes, exp.counts, exp.options.toResource != "", "foobar") + response := api.GenTopRoutesResponse(exp.routes, exp.counts, exp.options.toResource != "", "foobar") mockClient.TopRoutesResponseToReturn = response diff --git a/viz/cmd/stat.go b/viz/cmd/stat.go index 656091c4e1115..c9a6e27f8309e 100644 --- a/viz/cmd/stat.go +++ b/viz/cmd/stat.go @@ -11,13 +11,14 @@ import ( "text/tabwriter" "time" - "github.com/linkerd/linkerd2/controller/api/util" + coreUtil "github.com/linkerd/linkerd2/controller/api/util" "github.com/linkerd/linkerd2/pkg/cmd" pkgcmd "github.com/linkerd/linkerd2/pkg/cmd" "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" - api "github.com/linkerd/linkerd2/pkg/public" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + "github.com/linkerd/linkerd2/viz/metrics-api/util" + "github.com/linkerd/linkerd2/viz/pkg/api" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -166,7 +167,7 @@ If no resource name is specified, displays stats about all resources of the spec # Get all inbound stats to the test namespace. linkerd viz stat ns/test`, Args: cobra.MinimumNArgs(1), - ValidArgs: util.ValidTargets, + ValidArgs: coreUtil.ValidTargets, RunE: func(cmd *cobra.Command, args []string) error { if options.namespace == "" { options.namespace = pkgcmd.GetDefaultNamespace(kubeconfigPath, kubeContext) @@ -179,7 +180,7 @@ If no resource name is specified, displays stats about all resources of the spec // The gRPC client is concurrency-safe, so we can reuse it in all the following goroutines // /~https://github.com/grpc/grpc-go/issues/682 - client := api.CheckVizAPIClientOrExit(healthcheck.Options{ + client := api.CheckClientOrExit(healthcheck.Options{ ControlPlaneNamespace: controlPlaneNamespace, KubeConfig: kubeconfigPath, Impersonate: impersonate, @@ -685,20 +686,20 @@ func getNamePrefix(resourceType string) string { } func buildStatSummaryRequests(resources []string, options *statOptions) ([]*pb.StatSummaryRequest, error) { - targets, err := util.BuildResources(options.namespace, resources) + targets, err := coreUtil.BuildResources(options.namespace, resources) if err != nil { return nil, err } var toRes, fromRes *pb.Resource if options.toResource != "" { - toRes, err = util.BuildResource(options.toNamespace, options.toResource) + toRes, err = coreUtil.BuildResource(options.toNamespace, options.toResource) if err != nil { return nil, err } } if options.fromResource != "" { - fromRes, err = util.BuildResource(options.fromNamespace, options.fromResource) + fromRes, err = coreUtil.BuildResource(options.fromNamespace, options.fromResource) if err != nil { return nil, err } diff --git a/viz/cmd/stat_test.go b/viz/cmd/stat_test.go index 87bcd5dec67a1..d0c8dd0d17a17 100644 --- a/viz/cmd/stat_test.go +++ b/viz/cmd/stat_test.go @@ -3,13 +3,13 @@ package cmd import ( "testing" - "github.com/linkerd/linkerd2/controller/api/public" pkgcmd "github.com/linkerd/linkerd2/pkg/cmd" "github.com/linkerd/linkerd2/pkg/k8s" + api "github.com/linkerd/linkerd2/viz/metrics-api" ) type paramsExp struct { - counts *public.PodCounts + counts *api.PodCounts options *statOptions resNs []string file string @@ -19,7 +19,7 @@ func TestStat(t *testing.T) { options := newStatOptions() t.Run("Returns namespace stats", func(t *testing.T) { testStatCall(paramsExp{ - counts: &public.PodCounts{ + counts: &api.PodCounts{ MeshedPods: 1, RunningPods: 2, FailedPods: 0, @@ -32,7 +32,7 @@ func TestStat(t *testing.T) { t.Run("Returns pod stats", func(t *testing.T) { testStatCall(paramsExp{ - counts: &public.PodCounts{ + counts: &api.PodCounts{ Status: "Running", MeshedPods: 1, RunningPods: 1, @@ -55,7 +55,7 @@ func TestStat(t *testing.T) { options.outputFormat = jsonOutput t.Run("Returns namespace stats (json)", func(t *testing.T) { testStatCall(paramsExp{ - counts: &public.PodCounts{ + counts: &api.PodCounts{ MeshedPods: 1, RunningPods: 2, FailedPods: 0, @@ -78,7 +78,7 @@ func TestStat(t *testing.T) { options.allNamespaces = true t.Run("Returns all namespace stats", func(t *testing.T) { testStatCall(paramsExp{ - counts: &public.PodCounts{ + counts: &api.PodCounts{ MeshedPods: 1, RunningPods: 2, FailedPods: 0, @@ -92,7 +92,7 @@ func TestStat(t *testing.T) { options.outputFormat = jsonOutput t.Run("Returns all namespace stats (json)", func(t *testing.T) { testStatCall(paramsExp{ - counts: &public.PodCounts{ + counts: &api.PodCounts{ MeshedPods: 1, RunningPods: 2, FailedPods: 0, @@ -107,7 +107,7 @@ func TestStat(t *testing.T) { options.outputFormat = "wide" t.Run("Returns TCP stats", func(t *testing.T) { testStatCall(paramsExp{ - counts: &public.PodCounts{ + counts: &api.PodCounts{ MeshedPods: 1, RunningPods: 2, FailedPods: 0, @@ -228,10 +228,10 @@ func TestStat(t *testing.T) { } func testStatCall(exp paramsExp, resourceType string, t *testing.T) { - mockClient := &public.MockAPIClient{} - response := public.GenStatSummaryResponse("emoji", resourceType, exp.resNs, exp.counts, true, true) + mockClient := &api.MockAPIClient{} + response := api.GenStatSummaryResponse("emoji", resourceType, exp.resNs, exp.counts, true, true) if resourceType == k8s.TrafficSplit { - response = public.GenStatTsResponse("foo-split", resourceType, exp.resNs, true, true) + response = api.GenStatTsResponse("foo-split", resourceType, exp.resNs, true, true) } mockClient.StatSummaryResponseToReturn = response diff --git a/viz/cmd/tap.go b/viz/cmd/tap.go index 42a18a26a341b..30ee7976d9b20 100644 --- a/viz/cmd/tap.go +++ b/viz/cmd/tap.go @@ -11,6 +11,7 @@ import ( "github.com/golang/protobuf/ptypes/duration" "github.com/linkerd/linkerd2/controller/api/util" + netPb "github.com/linkerd/linkerd2/controller/gen/common/net" "github.com/linkerd/linkerd2/pkg/addr" pkgcmd "github.com/linkerd/linkerd2/pkg/cmd" "github.com/linkerd/linkerd2/pkg/k8s" @@ -536,7 +537,7 @@ func dst(event *pb.TapEvent) peer { } type peer struct { - address *pb.TcpAddress + address *netPb.TcpAddress labels map[string]string direction string } diff --git a/viz/cmd/tap_test.go b/viz/cmd/tap_test.go index 639c232db01c8..a9522b718333a 100644 --- a/viz/cmd/tap_test.go +++ b/viz/cmd/tap_test.go @@ -10,6 +10,7 @@ import ( "github.com/golang/protobuf/ptypes/duration" "github.com/linkerd/linkerd2/controller/api/util" + netPb "github.com/linkerd/linkerd2/controller/gen/common/net" "github.com/linkerd/linkerd2/pkg/addr" "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/protohttp" @@ -264,11 +265,11 @@ func TestEventToString(t *testing.T) { return &pb.TapEvent{ ProxyDirection: pb.TapEvent_OUTBOUND, - Source: &pb.TcpAddress{ + Source: &netPb.TcpAddress{ Ip: addr.PublicIPV4(1, 2, 3, 4), Port: 5555, }, - Destination: &pb.TcpAddress{ + Destination: &netPb.TcpAddress{ Ip: addr.PublicIPV4(2, 3, 4, 5), Port: 6666, }, diff --git a/viz/cmd/testdata/install_default.golden b/viz/cmd/testdata/install_default.golden index 17f9f291dcd95..eed72d177efcb 100644 --- a/viz/cmd/testdata/install_default.golden +++ b/viz/cmd/testdata/install_default.golden @@ -13,6 +13,59 @@ metadata: linkerd.io/inject: enabled --- ### +### Metrics API RBAC +### +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-linkerd-viz-metrics-api + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +rules: +- apiGroups: ["extensions", "apps"] + resources: ["daemonsets", "deployments", "replicasets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["extensions", "batch"] + resources: ["cronjobs", "jobs"] + verbs: ["list" , "get", "watch"] +- apiGroups: [""] + resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"] + verbs: ["list", "get", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["split.smi-spec.io"] + resources: ["trafficsplits"] + verbs: ["list", "get", "watch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-linkerd-viz-metrics-api + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-linkerd-viz-metrics-api +subjects: +- kind: ServiceAccount + name: linkerd-metrics-api + namespace: linkerd-viz +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: linkerd-viz + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +--- +### ### Grafana RBAC ### --- @@ -337,6 +390,89 @@ subjects: namespace: linkerd-viz --- ### +### Metrics API +### +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: linkerd-viz + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api + annotations: + linkerd.io/created-by: linkerd/helm dev-undefined +spec: + type: ClusterIP + selector: + linkerd.io/extension: linkerd-viz + component: metrics-api + ports: + - name: http + port: 8085 + targetPort: 8085 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + linkerd.io/created-by: linkerd/helm dev-undefined + labels: + linkerd.io/extension: linkerd-viz + app.kubernetes.io/name: metrics-api + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: dev-undefined + component: metrics-api + name: linkerd-metrics-api + namespace: linkerd-viz +spec: + replicas: 1 + selector: + matchLabels: + linkerd.io/extension: linkerd-viz + component: metrics-api + template: + metadata: + annotations: + checksum/config: b9049bed0ca52f3d63f34e03d87cfdd38bd81f6066310d0bf64d2d3f380d5733 + linkerd.io/created-by: linkerd/helm dev-undefined + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api + spec: + nodeSelector: + beta.kubernetes.io/os: linux + containers: + - args: + - -controller-namespace=linkerd + - -log-level=info + - -cluster-domain=cluster.local + - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 + image: ghcr.io/linkerd/metrics-api:dev-undefined + imagePullPolicy: + livenessProbe: + httpGet: + path: /ping + port: 9995 + initialDelaySeconds: 10 + name: metrics-api + ports: + - containerPort: 8085 + name: http + - containerPort: 9995 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9995 + resources: + securityContext: + runAsUser: 2103 + serviceAccountName: linkerd-metrics-api +--- +### ### Grafana ### --- @@ -1060,10 +1196,12 @@ spec: beta.kubernetes.io/os: linux containers: - args: - - -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085 + - -linkerd-controller-api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085 + - -linkerd-metrics-api-addr=linkerd-metrics-api.linkerd-viz.svc.cluster.local:8085 - -cluster-domain=cluster.local - -grafana-addr=linkerd-grafana.linkerd-viz.svc.cluster.local:3000 - -controller-namespace=linkerd + - -viz-namespace=linkerd-viz - -log-level=info - -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd-viz\.svc\.cluster\.local|linkerd-web\.linkerd-viz\.svc|\[::1\])(:\d+)?$ image: ghcr.io/linkerd/web:dev-undefined diff --git a/viz/cmd/testdata/install_prometheus_disabled.golden b/viz/cmd/testdata/install_prometheus_disabled.golden index 59df1ebcec049..084f78b798a7f 100644 --- a/viz/cmd/testdata/install_prometheus_disabled.golden +++ b/viz/cmd/testdata/install_prometheus_disabled.golden @@ -13,6 +13,59 @@ metadata: linkerd.io/inject: enabled --- ### +### Metrics API RBAC +### +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-linkerd-viz-metrics-api + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +rules: +- apiGroups: ["extensions", "apps"] + resources: ["daemonsets", "deployments", "replicasets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["extensions", "batch"] + resources: ["cronjobs", "jobs"] + verbs: ["list" , "get", "watch"] +- apiGroups: [""] + resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"] + verbs: ["list", "get", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["split.smi-spec.io"] + resources: ["trafficsplits"] + verbs: ["list", "get", "watch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-linkerd-viz-metrics-api + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-linkerd-viz-metrics-api +subjects: +- kind: ServiceAccount + name: linkerd-metrics-api + namespace: linkerd-viz +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: linkerd-viz + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +--- +### ### Grafana RBAC ### --- @@ -292,6 +345,89 @@ subjects: namespace: linkerd-viz --- ### +### Metrics API +### +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: linkerd-viz + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api + annotations: + linkerd.io/created-by: linkerd/helm dev-undefined +spec: + type: ClusterIP + selector: + linkerd.io/extension: linkerd-viz + component: metrics-api + ports: + - name: http + port: 8085 + targetPort: 8085 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + linkerd.io/created-by: linkerd/helm dev-undefined + labels: + linkerd.io/extension: linkerd-viz + app.kubernetes.io/name: metrics-api + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: dev-undefined + component: metrics-api + name: linkerd-metrics-api + namespace: linkerd-viz +spec: + replicas: 1 + selector: + matchLabels: + linkerd.io/extension: linkerd-viz + component: metrics-api + template: + metadata: + annotations: + checksum/config: b9049bed0ca52f3d63f34e03d87cfdd38bd81f6066310d0bf64d2d3f380d5733 + linkerd.io/created-by: linkerd/helm dev-undefined + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api + spec: + nodeSelector: + beta.kubernetes.io/os: linux + containers: + - args: + - -controller-namespace=linkerd + - -log-level=info + - -cluster-domain=cluster.local + - -prometheus-url=external-prom.com + image: ghcr.io/linkerd/metrics-api:dev-undefined + imagePullPolicy: + livenessProbe: + httpGet: + path: /ping + port: 9995 + initialDelaySeconds: 10 + name: metrics-api + ports: + - containerPort: 8085 + name: http + - containerPort: 9995 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9995 + resources: + securityContext: + runAsUser: 2103 + serviceAccountName: linkerd-metrics-api +--- +### ### Grafana ### --- @@ -768,10 +904,12 @@ spec: beta.kubernetes.io/os: linux containers: - args: - - -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085 + - -linkerd-controller-api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085 + - -linkerd-metrics-api-addr=linkerd-metrics-api.linkerd-viz.svc.cluster.local:8085 - -cluster-domain=cluster.local - -grafana-addr=linkerd-grafana.linkerd-viz.svc.cluster.local:3000 - -controller-namespace=linkerd + - -viz-namespace=linkerd-viz - -log-level=info - -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd-viz\.svc\.cluster\.local|linkerd-web\.linkerd-viz\.svc|\[::1\])(:\d+)?$ image: ghcr.io/linkerd/web:dev-undefined diff --git a/viz/cmd/testdata/install_proxy_resources.golden b/viz/cmd/testdata/install_proxy_resources.golden index 796a125702be9..ee3402e376874 100644 --- a/viz/cmd/testdata/install_proxy_resources.golden +++ b/viz/cmd/testdata/install_proxy_resources.golden @@ -13,6 +13,59 @@ metadata: linkerd.io/inject: enabled --- ### +### Metrics API RBAC +### +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-linkerd-viz-metrics-api + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +rules: +- apiGroups: ["extensions", "apps"] + resources: ["daemonsets", "deployments", "replicasets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["extensions", "batch"] + resources: ["cronjobs", "jobs"] + verbs: ["list" , "get", "watch"] +- apiGroups: [""] + resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"] + verbs: ["list", "get", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["split.smi-spec.io"] + resources: ["trafficsplits"] + verbs: ["list", "get", "watch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-linkerd-viz-metrics-api + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-linkerd-viz-metrics-api +subjects: +- kind: ServiceAccount + name: linkerd-metrics-api + namespace: linkerd-viz +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: linkerd-viz + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api +--- +### ### Grafana RBAC ### --- @@ -337,6 +390,89 @@ subjects: namespace: linkerd-viz --- ### +### Metrics API +### +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-metrics-api + namespace: linkerd-viz + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api + annotations: + linkerd.io/created-by: linkerd/helm dev-undefined +spec: + type: ClusterIP + selector: + linkerd.io/extension: linkerd-viz + component: metrics-api + ports: + - name: http + port: 8085 + targetPort: 8085 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + linkerd.io/created-by: linkerd/helm dev-undefined + labels: + linkerd.io/extension: linkerd-viz + app.kubernetes.io/name: metrics-api + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: dev-undefined + component: metrics-api + name: linkerd-metrics-api + namespace: linkerd-viz +spec: + replicas: 1 + selector: + matchLabels: + linkerd.io/extension: linkerd-viz + component: metrics-api + template: + metadata: + annotations: + checksum/config: b9049bed0ca52f3d63f34e03d87cfdd38bd81f6066310d0bf64d2d3f380d5733 + linkerd.io/created-by: linkerd/helm dev-undefined + labels: + linkerd.io/extension: linkerd-viz + component: metrics-api + spec: + nodeSelector: + beta.kubernetes.io/os: linux + containers: + - args: + - -controller-namespace=linkerd + - -log-level=info + - -cluster-domain=cluster.local + - -prometheus-url=http://linkerd-prometheus.linkerd-viz.svc.cluster.local:9090 + image: ghcr.io/linkerd/metrics-api:dev-undefined + imagePullPolicy: + livenessProbe: + httpGet: + path: /ping + port: 9995 + initialDelaySeconds: 10 + name: metrics-api + ports: + - containerPort: 8085 + name: http + - containerPort: 9995 + name: admin-http + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: 9995 + resources: + securityContext: + runAsUser: 2103 + serviceAccountName: linkerd-metrics-api +--- +### ### Grafana ### --- @@ -1076,10 +1212,12 @@ spec: beta.kubernetes.io/os: linux containers: - args: - - -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085 + - -linkerd-controller-api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085 + - -linkerd-metrics-api-addr=linkerd-metrics-api.linkerd-viz.svc.cluster.local:8085 - -cluster-domain=cluster.local - -grafana-addr=linkerd-grafana.linkerd-viz.svc.cluster.local:3000 - -controller-namespace=linkerd + - -viz-namespace=linkerd-viz - -log-level=info - -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd-viz\.svc\.cluster\.local|linkerd-web\.linkerd-viz\.svc|\[::1\])(:\d+)?$ image: ghcr.io/linkerd/web:dev-undefined diff --git a/viz/cmd/top.go b/viz/cmd/top.go index b154ef539596e..d4c67078bf608 100644 --- a/viz/cmd/top.go +++ b/viz/cmd/top.go @@ -11,13 +11,13 @@ import ( "time" "github.com/golang/protobuf/ptypes" - "github.com/linkerd/linkerd2/controller/api/public" "github.com/linkerd/linkerd2/controller/api/util" "github.com/linkerd/linkerd2/pkg/addr" pkgcmd "github.com/linkerd/linkerd2/pkg/cmd" "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/protohttp" "github.com/linkerd/linkerd2/pkg/tap" + api "github.com/linkerd/linkerd2/viz/metrics-api" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" runewidth "github.com/mattn/go-runewidth" termbox "github.com/nsf/termbox-go" @@ -544,7 +544,7 @@ func newRow(req topRequest) (tableRow, error) { path := req.reqInit.GetPath() route := req.event.GetRouteMeta().GetLabels()["route"] if route == "" { - route = public.DefaultRouteName + route = api.DefaultRouteName } method := req.reqInit.GetMethod().GetRegistered().String() source := stripPort(addr.PublicAddressToString(req.event.GetSource())) diff --git a/viz/cmd/uninstall.go b/viz/cmd/uninstall.go index a0d3ca1a7d4f4..7dc86a29fd48d 100644 --- a/viz/cmd/uninstall.go +++ b/viz/cmd/uninstall.go @@ -34,13 +34,13 @@ func uninstallRunE(ctx context.Context) error { return err } - vizNamespace, err := getVizNamespace(ctx, k8sAPI) + vizNs, err := k8sAPI.GetNamespaceWithExtensionLabel(ctx, ExtensionName) if err != nil { return err } resources, err := resource.FetchKubernetesResources(ctx, k8sAPI, - metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", k8s.LinkerdExtensionLabel, vizNamespace)}, + metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", k8s.LinkerdExtensionLabel, vizNs.Name)}, ) if err != nil { return err diff --git a/viz/metrics-api/Dockerfile b/viz/metrics-api/Dockerfile new file mode 100644 index 0000000000000..34b6d86c2b9e0 --- /dev/null +++ b/viz/metrics-api/Dockerfile @@ -0,0 +1,29 @@ +ARG BUILDPLATFORM=linux/amd64 + +# Precompile key slow-to-build dependencies +FROM --platform=$BUILDPLATFORM golang:1.14.2-alpine as go-deps +WORKDIR /linkerd-build +COPY go.mod go.sum ./ +COPY bin/install-deps bin/ +RUN go mod download +ARG TARGETARCH +RUN ./bin/install-deps $TARGETARCH + +## compile metrics-apiservice +FROM go-deps as golang +WORKDIR /linkerd-build +COPY pkg pkg +COPY controller controller +COPY viz/metrics-api viz/metrics-api +COPY viz/pkg viz/pkg + +ARG TARGETARCH +RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/metrics-api -tags prod -mod=readonly -ldflags "-s -w" ./viz/metrics-api/cmd + +## package runtime +FROM scratch +ENV PATH=$PATH:/go/bin +COPY LICENSE /linkerd/LICENSE +COPY --from=golang /out/metrics-api /go/bin/metrics-api + +ENTRYPOINT ["/go/bin/metrics-api"] diff --git a/viz/metrics-api/client/client.go b/viz/metrics-api/client/client.go new file mode 100644 index 0000000000000..03bd6c6508946 --- /dev/null +++ b/viz/metrics-api/client/client.go @@ -0,0 +1,190 @@ +package client + +import ( + "bufio" + "bytes" + "context" + "fmt" + "net/http" + "net/url" + + "github.com/golang/protobuf/proto" + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + "github.com/linkerd/linkerd2/pkg/k8s" + "github.com/linkerd/linkerd2/pkg/protohttp" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + log "github.com/sirupsen/logrus" + "go.opencensus.io/plugin/ochttp" + "google.golang.org/grpc" +) + +const ( + // APIRoot is the API's root path. It must be absolute (with a leading slash) + APIRoot = "/" + + apiVersion = "v1" + + // APIPrefix is the prefix all the API endpoints must share. It must be relative + // (without a leading slash) + APIPrefix = "api/" + apiVersion + "/" + + apiPort = 8085 + apiDeployment = "linkerd-metrics-api" +) + +type grpcOverHTTPClient struct { + serverURL *url.URL + httpClient *http.Client + namespace string +} + +func (c *grpcOverHTTPClient) StatSummary(ctx context.Context, req *pb.StatSummaryRequest, _ ...grpc.CallOption) (*pb.StatSummaryResponse, error) { + var msg pb.StatSummaryResponse + err := c.apiRequest(ctx, "StatSummary", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) Edges(ctx context.Context, req *pb.EdgesRequest, _ ...grpc.CallOption) (*pb.EdgesResponse, error) { + var msg pb.EdgesResponse + err := c.apiRequest(ctx, "Edges", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) TopRoutes(ctx context.Context, req *pb.TopRoutesRequest, _ ...grpc.CallOption) (*pb.TopRoutesResponse, error) { + var msg pb.TopRoutesResponse + err := c.apiRequest(ctx, "TopRoutes", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) Gateways(ctx context.Context, req *pb.GatewaysRequest, _ ...grpc.CallOption) (*pb.GatewaysResponse, error) { + var msg pb.GatewaysResponse + err := c.apiRequest(ctx, "Gateways", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) SelfCheck(ctx context.Context, req *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) { + var msg healthcheckPb.SelfCheckResponse + err := c.apiRequest(ctx, "SelfCheck", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) ListPods(ctx context.Context, req *pb.ListPodsRequest, _ ...grpc.CallOption) (*pb.ListPodsResponse, error) { + var msg pb.ListPodsResponse + err := c.apiRequest(ctx, "ListPods", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) ListServices(ctx context.Context, req *pb.ListServicesRequest, _ ...grpc.CallOption) (*pb.ListServicesResponse, error) { + var msg pb.ListServicesResponse + err := c.apiRequest(ctx, "ListServices", req, &msg) + return &msg, err +} + +func (c *grpcOverHTTPClient) apiRequest(ctx context.Context, endpoint string, req proto.Message, protoResponse proto.Message) error { + url := c.endpointNameToPublicAPIURL(endpoint) + + log.Debugf("Making gRPC-over-HTTP call to [%s] [%+v]", url.String(), req) + httpRsp, err := c.post(ctx, url, req) + if err != nil { + return err + } + defer httpRsp.Body.Close() + log.Debugf("gRPC-over-HTTP call returned status [%s] and content length [%d]", httpRsp.Status, httpRsp.ContentLength) + + if err := protohttp.CheckIfResponseHasError(httpRsp); err != nil { + return err + } + + reader := bufio.NewReader(httpRsp.Body) + return protohttp.FromByteStreamToProtocolBuffers(reader, protoResponse) +} + +func (c *grpcOverHTTPClient) post(ctx context.Context, url *url.URL, req proto.Message) (*http.Response, error) { + reqBytes, err := proto.Marshal(req) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + http.MethodPost, + url.String(), + bytes.NewReader(reqBytes), + ) + if err != nil { + return nil, err + } + + rsp, err := c.httpClient.Do(httpReq.WithContext(ctx)) + if err != nil { + log.Debugf("Error invoking [%s]: %v", url.String(), err) + } else { + log.Debugf("Response from [%s] had headers: %v", url.String(), rsp.Header) + } + + return rsp, err +} + +func (c *grpcOverHTTPClient) endpointNameToPublicAPIURL(endpoint string) *url.URL { + return c.serverURL.ResolveReference(&url.URL{Path: endpoint}) +} + +func newClient(apiURL *url.URL, httpClientToUse *http.Client, namespace string) (pb.ApiClient, error) { + if !apiURL.IsAbs() { + return nil, fmt.Errorf("server URL must be absolute, was [%s]", apiURL.String()) + } + + serverURL := apiURL.ResolveReference(&url.URL{Path: APIPrefix}) + + log.Debugf("Expecting API to be served over [%s]", serverURL) + + return &grpcOverHTTPClient{ + serverURL: serverURL, + httpClient: httpClientToUse, + namespace: namespace, + }, nil +} + +// NewInternalClient creates a new Viz API client intended to run inside a +// Kubernetes cluster. +func NewInternalClient(namespace string, kubeAPIHost string) (pb.ApiClient, error) { + apiURL, err := url.Parse(fmt.Sprintf("http://%s/", kubeAPIHost)) + if err != nil { + return nil, err + } + + return newClient(apiURL, &http.Client{Transport: &ochttp.Transport{}}, namespace) +} + +// NewExternalClient creates a new Viz API client intended to run from +// outside a Kubernetes cluster. +func NewExternalClient(ctx context.Context, namespace string, kubeAPI *k8s.KubernetesAPI) (pb.ApiClient, error) { + portforward, err := k8s.NewPortForward( + ctx, + kubeAPI, + namespace, + apiDeployment, + "localhost", + 0, + apiPort, + false, + ) + if err != nil { + return nil, err + } + + apiURL, err := url.Parse(portforward.URLFor("")) + if err != nil { + return nil, err + } + + if err = portforward.Init(); err != nil { + return nil, err + } + + httpClientToUse, err := kubeAPI.NewClient() + if err != nil { + return nil, err + } + + return newClient(apiURL, httpClientToUse, namespace) +} diff --git a/viz/metrics-api/cmd/main.go b/viz/metrics-api/cmd/main.go new file mode 100644 index 0000000000000..a8fb4804fdc40 --- /dev/null +++ b/viz/metrics-api/cmd/main.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "flag" + "os" + "os/signal" + "strings" + "syscall" + + "github.com/linkerd/linkerd2/controller/k8s" + "github.com/linkerd/linkerd2/pkg/admin" + "github.com/linkerd/linkerd2/pkg/flags" + "github.com/linkerd/linkerd2/pkg/trace" + api "github.com/linkerd/linkerd2/viz/metrics-api" + promApi "github.com/prometheus/client_golang/api" + log "github.com/sirupsen/logrus" +) + +func main() { + cmd := flag.NewFlagSet("metrics-api", flag.ExitOnError) + + addr := cmd.String("addr", ":8085", "address to serve on") + kubeConfigPath := cmd.String("kubeconfig", "", "path to kube config") + prometheusURL := cmd.String("prometheus-url", "", "prometheus url") + metricsAddr := cmd.String("metrics-addr", ":9995", "address to serve scrapable metrics on") + controllerNamespace := cmd.String("controller-namespace", "linkerd", "namespace in which Linkerd is installed") + ignoredNamespaces := cmd.String("ignore-namespaces", "kube-system", "comma separated list of namespaces to not list pods from") + clusterDomain := cmd.String("cluster-domain", "cluster.local", "kubernetes cluster domain") + + traceCollector := flags.AddTraceFlags(cmd) + + flags.ConfigureAndParse(cmd, os.Args[1:]) + ctx := context.Background() + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGTERM) + + k8sAPI, err := k8s.InitializeAPI( + ctx, + *kubeConfigPath, + true, + k8s.CJ, k8s.DS, k8s.Deploy, k8s.Job, k8s.NS, k8s.Pod, k8s.RC, k8s.RS, k8s.Svc, k8s.SS, k8s.SP, k8s.TS, + ) + if err != nil { + log.Fatalf("Failed to initialize K8s API: %s", err) + } + + var prometheusClient promApi.Client + if *prometheusURL != "" { + prometheusClient, err = promApi.NewClient(promApi.Config{Address: *prometheusURL}) + if err != nil { + log.Fatal(err.Error()) + } + } + + log.Infof("prometheusClient: %#v", prometheusClient) + log.Info("Using cluster domain: ", *clusterDomain) + + if *traceCollector != "" { + if err := trace.InitializeTracing("linkerd-public-api", *traceCollector); err != nil { + log.Warnf("failed to initialize tracing: %s", err) + } + } + + server := api.NewServer( + *addr, + prometheusClient, + k8sAPI, + *controllerNamespace, + *clusterDomain, + strings.Split(*ignoredNamespaces, ","), + ) + + k8sAPI.Sync(nil) // blocks until caches are synced + + go func() { + log.Infof("starting HTTP server on %+v", *addr) + server.ListenAndServe() + }() + + go admin.StartServer(*metricsAddr) + + <-stop + + log.Infof("shutting down HTTP server on %+v", *addr) + server.Shutdown(ctx) +} diff --git a/controller/api/public/edges.go b/viz/metrics-api/edges.go similarity index 99% rename from controller/api/public/edges.go rename to viz/metrics-api/edges.go index 0069d8db41e7e..1f4627ce1d14f 100644 --- a/controller/api/public/edges.go +++ b/viz/metrics-api/edges.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/controller/api/public/edges_test.go b/viz/metrics-api/edges_test.go similarity index 99% rename from controller/api/public/edges_test.go rename to viz/metrics-api/edges_test.go index 27865b4e6aa34..060fc9437b823 100644 --- a/controller/api/public/edges_test.go +++ b/viz/metrics-api/edges_test.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/controller/api/public/gateways.go b/viz/metrics-api/gateways.go similarity index 99% rename from controller/api/public/gateways.go rename to viz/metrics-api/gateways.go index 4d4a0f09b4a17..1cf04844b34d1 100644 --- a/controller/api/public/gateways.go +++ b/viz/metrics-api/gateways.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/viz/metrics-api/gen/viz/viz.pb.go b/viz/metrics-api/gen/viz/viz.pb.go index b726ee64504a0..0fbbe69a13c22 100644 --- a/viz/metrics-api/gen/viz/viz.pb.go +++ b/viz/metrics-api/gen/viz/viz.pb.go @@ -10,7 +10,8 @@ import ( context "context" proto "github.com/golang/protobuf/proto" duration "github.com/golang/protobuf/ptypes/duration" - healthcheck "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz/healthcheck" + healthcheck "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + net "github.com/linkerd/linkerd2/controller/gen/common/net" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -190,7 +191,7 @@ func (x TapEvent_ProxyDirection) Number() protoreflect.EnumNumber { // Deprecated: Use TapEvent_ProxyDirection.Descriptor instead. func (TapEvent_ProxyDirection) EnumDescriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 0} + return file_viz_proto_rawDescGZIP(), []int{13, 0} } type Empty struct { @@ -1148,196 +1149,6 @@ func (x *Headers) GetHeaders() []*Headers_Header { return nil } -type IPAddress struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Ip: - // *IPAddress_Ipv4 - // *IPAddress_Ipv6 - Ip isIPAddress_Ip `protobuf_oneof:"ip"` -} - -func (x *IPAddress) Reset() { - *x = IPAddress{} - if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IPAddress) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IPAddress) ProtoMessage() {} - -func (x *IPAddress) ProtoReflect() protoreflect.Message { - mi := &file_viz_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 IPAddress.ProtoReflect.Descriptor instead. -func (*IPAddress) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{12} -} - -func (m *IPAddress) GetIp() isIPAddress_Ip { - if m != nil { - return m.Ip - } - return nil -} - -func (x *IPAddress) GetIpv4() uint32 { - if x, ok := x.GetIp().(*IPAddress_Ipv4); ok { - return x.Ipv4 - } - return 0 -} - -func (x *IPAddress) GetIpv6() *IPv6 { - if x, ok := x.GetIp().(*IPAddress_Ipv6); ok { - return x.Ipv6 - } - return nil -} - -type isIPAddress_Ip interface { - isIPAddress_Ip() -} - -type IPAddress_Ipv4 struct { - Ipv4 uint32 `protobuf:"fixed32,1,opt,name=ipv4,proto3,oneof"` -} - -type IPAddress_Ipv6 struct { - Ipv6 *IPv6 `protobuf:"bytes,2,opt,name=ipv6,proto3,oneof"` -} - -func (*IPAddress_Ipv4) isIPAddress_Ip() {} - -func (*IPAddress_Ipv6) isIPAddress_Ip() {} - -type IPv6 struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - First uint64 `protobuf:"fixed64,1,opt,name=first,proto3" json:"first,omitempty"` // hextets 1-4 - Last uint64 `protobuf:"fixed64,2,opt,name=last,proto3" json:"last,omitempty"` // hextets 5-8 -} - -func (x *IPv6) Reset() { - *x = IPv6{} - if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IPv6) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IPv6) ProtoMessage() {} - -func (x *IPv6) ProtoReflect() protoreflect.Message { - mi := &file_viz_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 IPv6.ProtoReflect.Descriptor instead. -func (*IPv6) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{13} -} - -func (x *IPv6) GetFirst() uint64 { - if x != nil { - return x.First - } - return 0 -} - -func (x *IPv6) GetLast() uint64 { - if x != nil { - return x.Last - } - return 0 -} - -type TcpAddress struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ip *IPAddress `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` -} - -func (x *TcpAddress) Reset() { - *x = TcpAddress{} - if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TcpAddress) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpAddress) ProtoMessage() {} - -func (x *TcpAddress) ProtoReflect() protoreflect.Message { - mi := &file_viz_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 TcpAddress.ProtoReflect.Descriptor instead. -func (*TcpAddress) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{14} -} - -func (x *TcpAddress) GetIp() *IPAddress { - if x != nil { - return x.Ip - } - return nil -} - -func (x *TcpAddress) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - type Eos struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1352,7 +1163,7 @@ type Eos struct { func (x *Eos) Reset() { *x = Eos{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[15] + mi := &file_viz_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1365,7 +1176,7 @@ func (x *Eos) String() string { func (*Eos) ProtoMessage() {} func (x *Eos) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[15] + mi := &file_viz_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1378,7 +1189,7 @@ func (x *Eos) ProtoReflect() protoreflect.Message { // Deprecated: Use Eos.ProtoReflect.Descriptor instead. func (*Eos) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{15} + return file_viz_proto_rawDescGZIP(), []int{12} } func (m *Eos) GetEnd() isEos_End { @@ -1424,9 +1235,9 @@ type TapEvent struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Source *TcpAddress `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + Source *net.TcpAddress `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` SourceMeta *TapEvent_EndpointMeta `protobuf:"bytes,5,opt,name=source_meta,json=sourceMeta,proto3" json:"source_meta,omitempty"` - Destination *TcpAddress `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` + Destination *net.TcpAddress `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` DestinationMeta *TapEvent_EndpointMeta `protobuf:"bytes,4,opt,name=destination_meta,json=destinationMeta,proto3" json:"destination_meta,omitempty"` RouteMeta *TapEvent_RouteMeta `protobuf:"bytes,7,opt,name=route_meta,json=routeMeta,proto3" json:"route_meta,omitempty"` ProxyDirection TapEvent_ProxyDirection `protobuf:"varint,6,opt,name=proxy_direction,json=proxyDirection,proto3,enum=linkerd2.viz.TapEvent_ProxyDirection" json:"proxy_direction,omitempty"` @@ -1438,7 +1249,7 @@ type TapEvent struct { func (x *TapEvent) Reset() { *x = TapEvent{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[16] + mi := &file_viz_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1451,7 +1262,7 @@ func (x *TapEvent) String() string { func (*TapEvent) ProtoMessage() {} func (x *TapEvent) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[16] + mi := &file_viz_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1464,10 +1275,10 @@ func (x *TapEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent.ProtoReflect.Descriptor instead. func (*TapEvent) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16} + return file_viz_proto_rawDescGZIP(), []int{13} } -func (x *TapEvent) GetSource() *TcpAddress { +func (x *TapEvent) GetSource() *net.TcpAddress { if x != nil { return x.Source } @@ -1481,7 +1292,7 @@ func (x *TapEvent) GetSourceMeta() *TapEvent_EndpointMeta { return nil } -func (x *TapEvent) GetDestination() *TcpAddress { +func (x *TapEvent) GetDestination() *net.TcpAddress { if x != nil { return x.Destination } @@ -1544,7 +1355,7 @@ type ApiError struct { func (x *ApiError) Reset() { *x = ApiError{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[17] + mi := &file_viz_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1557,7 +1368,7 @@ func (x *ApiError) String() string { func (*ApiError) ProtoMessage() {} func (x *ApiError) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[17] + mi := &file_viz_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1570,7 +1381,7 @@ func (x *ApiError) ProtoReflect() protoreflect.Message { // Deprecated: Use ApiError.ProtoReflect.Descriptor instead. func (*ApiError) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{17} + return file_viz_proto_rawDescGZIP(), []int{14} } func (x *ApiError) GetError() string { @@ -1591,7 +1402,7 @@ type PodErrors struct { func (x *PodErrors) Reset() { *x = PodErrors{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[18] + mi := &file_viz_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1604,7 +1415,7 @@ func (x *PodErrors) String() string { func (*PodErrors) ProtoMessage() {} func (x *PodErrors) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[18] + mi := &file_viz_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1617,7 +1428,7 @@ func (x *PodErrors) ProtoReflect() protoreflect.Message { // Deprecated: Use PodErrors.ProtoReflect.Descriptor instead. func (*PodErrors) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{18} + return file_viz_proto_rawDescGZIP(), []int{15} } func (x *PodErrors) GetErrors() []*PodErrors_PodError { @@ -1650,7 +1461,7 @@ type Resource struct { func (x *Resource) Reset() { *x = Resource{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[19] + mi := &file_viz_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1663,7 +1474,7 @@ func (x *Resource) String() string { func (*Resource) ProtoMessage() {} func (x *Resource) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[19] + mi := &file_viz_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1676,7 +1487,7 @@ func (x *Resource) ProtoReflect() protoreflect.Message { // Deprecated: Use Resource.ProtoReflect.Descriptor instead. func (*Resource) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{19} + return file_viz_proto_rawDescGZIP(), []int{16} } func (x *Resource) GetNamespace() string { @@ -1718,7 +1529,7 @@ type ResourceSelection struct { func (x *ResourceSelection) Reset() { *x = ResourceSelection{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[20] + mi := &file_viz_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1731,7 +1542,7 @@ func (x *ResourceSelection) String() string { func (*ResourceSelection) ProtoMessage() {} func (x *ResourceSelection) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[20] + mi := &file_viz_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1744,7 +1555,7 @@ func (x *ResourceSelection) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSelection.ProtoReflect.Descriptor instead. func (*ResourceSelection) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{20} + return file_viz_proto_rawDescGZIP(), []int{17} } func (x *ResourceSelection) GetResource() *Resource { @@ -1773,7 +1584,7 @@ type ResourceError struct { func (x *ResourceError) Reset() { *x = ResourceError{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[21] + mi := &file_viz_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1786,7 +1597,7 @@ func (x *ResourceError) String() string { func (*ResourceError) ProtoMessage() {} func (x *ResourceError) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[21] + mi := &file_viz_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1799,7 +1610,7 @@ func (x *ResourceError) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceError.ProtoReflect.Descriptor instead. func (*ResourceError) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{21} + return file_viz_proto_rawDescGZIP(), []int{18} } func (x *ResourceError) GetResource() *Resource { @@ -1835,7 +1646,7 @@ type StatSummaryRequest struct { func (x *StatSummaryRequest) Reset() { *x = StatSummaryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[22] + mi := &file_viz_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1848,7 +1659,7 @@ func (x *StatSummaryRequest) String() string { func (*StatSummaryRequest) ProtoMessage() {} func (x *StatSummaryRequest) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[22] + mi := &file_viz_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1861,7 +1672,7 @@ func (x *StatSummaryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatSummaryRequest.ProtoReflect.Descriptor instead. func (*StatSummaryRequest) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{22} + return file_viz_proto_rawDescGZIP(), []int{19} } func (x *StatSummaryRequest) GetSelector() *ResourceSelection { @@ -1956,7 +1767,7 @@ type StatSummaryResponse struct { func (x *StatSummaryResponse) Reset() { *x = StatSummaryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[23] + mi := &file_viz_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1969,7 +1780,7 @@ func (x *StatSummaryResponse) String() string { func (*StatSummaryResponse) ProtoMessage() {} func (x *StatSummaryResponse) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[23] + mi := &file_viz_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1982,7 +1793,7 @@ func (x *StatSummaryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatSummaryResponse.ProtoReflect.Descriptor instead. func (*StatSummaryResponse) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{23} + return file_viz_proto_rawDescGZIP(), []int{20} } func (m *StatSummaryResponse) GetResponse() isStatSummaryResponse_Response { @@ -2039,7 +1850,7 @@ type BasicStats struct { func (x *BasicStats) Reset() { *x = BasicStats{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[24] + mi := &file_viz_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2052,7 +1863,7 @@ func (x *BasicStats) String() string { func (*BasicStats) ProtoMessage() {} func (x *BasicStats) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[24] + mi := &file_viz_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2065,7 +1876,7 @@ func (x *BasicStats) ProtoReflect() protoreflect.Message { // Deprecated: Use BasicStats.ProtoReflect.Descriptor instead. func (*BasicStats) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{24} + return file_viz_proto_rawDescGZIP(), []int{21} } func (x *BasicStats) GetSuccessCount() uint64 { @@ -2133,7 +1944,7 @@ type TcpStats struct { func (x *TcpStats) Reset() { *x = TcpStats{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[25] + mi := &file_viz_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2146,7 +1957,7 @@ func (x *TcpStats) String() string { func (*TcpStats) ProtoMessage() {} func (x *TcpStats) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[25] + mi := &file_viz_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2159,7 +1970,7 @@ func (x *TcpStats) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpStats.ProtoReflect.Descriptor instead. func (*TcpStats) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{25} + return file_viz_proto_rawDescGZIP(), []int{22} } func (x *TcpStats) GetOpenConnections() uint64 { @@ -2196,7 +2007,7 @@ type TrafficSplitStats struct { func (x *TrafficSplitStats) Reset() { *x = TrafficSplitStats{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[26] + mi := &file_viz_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2209,7 +2020,7 @@ func (x *TrafficSplitStats) String() string { func (*TrafficSplitStats) ProtoMessage() {} func (x *TrafficSplitStats) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[26] + mi := &file_viz_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2222,7 +2033,7 @@ func (x *TrafficSplitStats) ProtoReflect() protoreflect.Message { // Deprecated: Use TrafficSplitStats.ProtoReflect.Descriptor instead. func (*TrafficSplitStats) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{26} + return file_viz_proto_rawDescGZIP(), []int{23} } func (x *TrafficSplitStats) GetApex() string { @@ -2259,7 +2070,7 @@ type StatTable struct { func (x *StatTable) Reset() { *x = StatTable{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[27] + mi := &file_viz_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2272,7 +2083,7 @@ func (x *StatTable) String() string { func (*StatTable) ProtoMessage() {} func (x *StatTable) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[27] + mi := &file_viz_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2285,7 +2096,7 @@ func (x *StatTable) ProtoReflect() protoreflect.Message { // Deprecated: Use StatTable.ProtoReflect.Descriptor instead. func (*StatTable) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{27} + return file_viz_proto_rawDescGZIP(), []int{24} } func (m *StatTable) GetTable() isStatTable_Table { @@ -2323,7 +2134,7 @@ type EdgesRequest struct { func (x *EdgesRequest) Reset() { *x = EdgesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[28] + mi := &file_viz_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2336,7 +2147,7 @@ func (x *EdgesRequest) String() string { func (*EdgesRequest) ProtoMessage() {} func (x *EdgesRequest) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[28] + mi := &file_viz_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2349,7 +2160,7 @@ func (x *EdgesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EdgesRequest.ProtoReflect.Descriptor instead. func (*EdgesRequest) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{28} + return file_viz_proto_rawDescGZIP(), []int{25} } func (x *EdgesRequest) GetSelector() *ResourceSelection { @@ -2373,7 +2184,7 @@ type EdgesResponse struct { func (x *EdgesResponse) Reset() { *x = EdgesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[29] + mi := &file_viz_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2386,7 +2197,7 @@ func (x *EdgesResponse) String() string { func (*EdgesResponse) ProtoMessage() {} func (x *EdgesResponse) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[29] + mi := &file_viz_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2399,7 +2210,7 @@ func (x *EdgesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EdgesResponse.ProtoReflect.Descriptor instead. func (*EdgesResponse) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{29} + return file_viz_proto_rawDescGZIP(), []int{26} } func (m *EdgesResponse) GetResponse() isEdgesResponse_Response { @@ -2454,7 +2265,7 @@ type Edge struct { func (x *Edge) Reset() { *x = Edge{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[30] + mi := &file_viz_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2467,7 +2278,7 @@ func (x *Edge) String() string { func (*Edge) ProtoMessage() {} func (x *Edge) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[30] + mi := &file_viz_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2480,7 +2291,7 @@ func (x *Edge) ProtoReflect() protoreflect.Message { // Deprecated: Use Edge.ProtoReflect.Descriptor instead. func (*Edge) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{30} + return file_viz_proto_rawDescGZIP(), []int{27} } func (x *Edge) GetSrc() *Resource { @@ -2534,7 +2345,7 @@ type TopRoutesRequest struct { func (x *TopRoutesRequest) Reset() { *x = TopRoutesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[31] + mi := &file_viz_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2547,7 +2358,7 @@ func (x *TopRoutesRequest) String() string { func (*TopRoutesRequest) ProtoMessage() {} func (x *TopRoutesRequest) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[31] + mi := &file_viz_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2560,7 +2371,7 @@ func (x *TopRoutesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TopRoutesRequest.ProtoReflect.Descriptor instead. func (*TopRoutesRequest) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{31} + return file_viz_proto_rawDescGZIP(), []int{28} } func (x *TopRoutesRequest) GetSelector() *ResourceSelection { @@ -2628,7 +2439,7 @@ type TopRoutesResponse struct { func (x *TopRoutesResponse) Reset() { *x = TopRoutesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[32] + mi := &file_viz_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2641,7 +2452,7 @@ func (x *TopRoutesResponse) String() string { func (*TopRoutesResponse) ProtoMessage() {} func (x *TopRoutesResponse) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[32] + mi := &file_viz_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2654,7 +2465,7 @@ func (x *TopRoutesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TopRoutesResponse.ProtoReflect.Descriptor instead. func (*TopRoutesResponse) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{32} + return file_viz_proto_rawDescGZIP(), []int{29} } func (m *TopRoutesResponse) GetResponse() isTopRoutesResponse_Response { @@ -2706,7 +2517,7 @@ type RouteTable struct { func (x *RouteTable) Reset() { *x = RouteTable{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[33] + mi := &file_viz_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2719,7 +2530,7 @@ func (x *RouteTable) String() string { func (*RouteTable) ProtoMessage() {} func (x *RouteTable) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[33] + mi := &file_viz_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2732,7 +2543,7 @@ func (x *RouteTable) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteTable.ProtoReflect.Descriptor instead. func (*RouteTable) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{33} + return file_viz_proto_rawDescGZIP(), []int{30} } func (x *RouteTable) GetRows() []*RouteTable_Row { @@ -2760,7 +2571,7 @@ type GatewaysTable struct { func (x *GatewaysTable) Reset() { *x = GatewaysTable{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[34] + mi := &file_viz_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2773,7 +2584,7 @@ func (x *GatewaysTable) String() string { func (*GatewaysTable) ProtoMessage() {} func (x *GatewaysTable) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[34] + mi := &file_viz_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2786,7 +2597,7 @@ func (x *GatewaysTable) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewaysTable.ProtoReflect.Descriptor instead. func (*GatewaysTable) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{34} + return file_viz_proto_rawDescGZIP(), []int{31} } func (x *GatewaysTable) GetRows() []*GatewaysTable_Row { @@ -2809,7 +2620,7 @@ type GatewaysRequest struct { func (x *GatewaysRequest) Reset() { *x = GatewaysRequest{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[35] + mi := &file_viz_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2822,7 +2633,7 @@ func (x *GatewaysRequest) String() string { func (*GatewaysRequest) ProtoMessage() {} func (x *GatewaysRequest) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[35] + mi := &file_viz_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2835,7 +2646,7 @@ func (x *GatewaysRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewaysRequest.ProtoReflect.Descriptor instead. func (*GatewaysRequest) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{35} + return file_viz_proto_rawDescGZIP(), []int{32} } func (x *GatewaysRequest) GetRemoteClusterName() string { @@ -2873,7 +2684,7 @@ type GatewaysResponse struct { func (x *GatewaysResponse) Reset() { *x = GatewaysResponse{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[36] + mi := &file_viz_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2886,7 +2697,7 @@ func (x *GatewaysResponse) String() string { func (*GatewaysResponse) ProtoMessage() {} func (x *GatewaysResponse) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[36] + mi := &file_viz_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2899,7 +2710,7 @@ func (x *GatewaysResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewaysResponse.ProtoReflect.Descriptor instead. func (*GatewaysResponse) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{36} + return file_viz_proto_rawDescGZIP(), []int{33} } func (m *GatewaysResponse) GetResponse() isGatewaysResponse_Response { @@ -2956,7 +2767,7 @@ type TapByResourceRequest_Match struct { func (x *TapByResourceRequest_Match) Reset() { *x = TapByResourceRequest_Match{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[37] + mi := &file_viz_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2969,7 +2780,7 @@ func (x *TapByResourceRequest_Match) String() string { func (*TapByResourceRequest_Match) ProtoMessage() {} func (x *TapByResourceRequest_Match) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[37] + mi := &file_viz_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3079,7 +2890,7 @@ type TapByResourceRequest_Extract struct { func (x *TapByResourceRequest_Extract) Reset() { *x = TapByResourceRequest_Extract{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[38] + mi := &file_viz_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3092,7 +2903,7 @@ func (x *TapByResourceRequest_Extract) String() string { func (*TapByResourceRequest_Extract) ProtoMessage() {} func (x *TapByResourceRequest_Extract) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[38] + mi := &file_viz_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3143,7 +2954,7 @@ type TapByResourceRequest_Match_Seq struct { func (x *TapByResourceRequest_Match_Seq) Reset() { *x = TapByResourceRequest_Match_Seq{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[39] + mi := &file_viz_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3156,7 +2967,7 @@ func (x *TapByResourceRequest_Match_Seq) String() string { func (*TapByResourceRequest_Match_Seq) ProtoMessage() {} func (x *TapByResourceRequest_Match_Seq) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[39] + mi := &file_viz_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3195,7 +3006,7 @@ type TapByResourceRequest_Match_Http struct { func (x *TapByResourceRequest_Match_Http) Reset() { *x = TapByResourceRequest_Match_Http{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[40] + mi := &file_viz_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3208,7 +3019,7 @@ func (x *TapByResourceRequest_Match_Http) String() string { func (*TapByResourceRequest_Match_Http) ProtoMessage() {} func (x *TapByResourceRequest_Match_Http) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[40] + mi := &file_viz_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3300,7 +3111,7 @@ type TapByResourceRequest_Extract_Http struct { func (x *TapByResourceRequest_Extract_Http) Reset() { *x = TapByResourceRequest_Extract_Http{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[41] + mi := &file_viz_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3313,7 +3124,7 @@ func (x *TapByResourceRequest_Extract_Http) String() string { func (*TapByResourceRequest_Extract_Http) ProtoMessage() {} func (x *TapByResourceRequest_Extract_Http) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[41] + mi := &file_viz_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3362,7 +3173,7 @@ type TapByResourceRequest_Extract_Http_Headers struct { func (x *TapByResourceRequest_Extract_Http_Headers) Reset() { *x = TapByResourceRequest_Extract_Http_Headers{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[42] + mi := &file_viz_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3375,7 +3186,7 @@ func (x *TapByResourceRequest_Extract_Http_Headers) String() string { func (*TapByResourceRequest_Extract_Http_Headers) ProtoMessage() {} func (x *TapByResourceRequest_Extract_Http_Headers) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[42] + mi := &file_viz_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3411,7 +3222,7 @@ type Headers_Header struct { func (x *Headers_Header) Reset() { *x = Headers_Header{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[43] + mi := &file_viz_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3424,7 +3235,7 @@ func (x *Headers_Header) String() string { func (*Headers_Header) ProtoMessage() {} func (x *Headers_Header) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[43] + mi := &file_viz_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3495,7 +3306,7 @@ type TapEvent_EndpointMeta struct { func (x *TapEvent_EndpointMeta) Reset() { *x = TapEvent_EndpointMeta{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[44] + mi := &file_viz_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3508,7 +3319,7 @@ func (x *TapEvent_EndpointMeta) String() string { func (*TapEvent_EndpointMeta) ProtoMessage() {} func (x *TapEvent_EndpointMeta) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[44] + mi := &file_viz_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3521,7 +3332,7 @@ func (x *TapEvent_EndpointMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_EndpointMeta.ProtoReflect.Descriptor instead. func (*TapEvent_EndpointMeta) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 0} + return file_viz_proto_rawDescGZIP(), []int{13, 0} } func (x *TapEvent_EndpointMeta) GetLabels() map[string]string { @@ -3542,7 +3353,7 @@ type TapEvent_RouteMeta struct { func (x *TapEvent_RouteMeta) Reset() { *x = TapEvent_RouteMeta{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[45] + mi := &file_viz_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3555,7 +3366,7 @@ func (x *TapEvent_RouteMeta) String() string { func (*TapEvent_RouteMeta) ProtoMessage() {} func (x *TapEvent_RouteMeta) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[45] + mi := &file_viz_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3568,7 +3379,7 @@ func (x *TapEvent_RouteMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_RouteMeta.ProtoReflect.Descriptor instead. func (*TapEvent_RouteMeta) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 1} + return file_viz_proto_rawDescGZIP(), []int{13, 1} } func (x *TapEvent_RouteMeta) GetLabels() map[string]string { @@ -3593,7 +3404,7 @@ type TapEvent_Http struct { func (x *TapEvent_Http) Reset() { *x = TapEvent_Http{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[46] + mi := &file_viz_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3606,7 +3417,7 @@ func (x *TapEvent_Http) String() string { func (*TapEvent_Http) ProtoMessage() {} func (x *TapEvent_Http) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[46] + mi := &file_viz_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3619,7 +3430,7 @@ func (x *TapEvent_Http) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_Http.ProtoReflect.Descriptor instead. func (*TapEvent_Http) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 2} + return file_viz_proto_rawDescGZIP(), []int{13, 2} } func (m *TapEvent_Http) GetEvent() isTapEvent_Http_Event { @@ -3686,7 +3497,7 @@ type TapEvent_Http_StreamId struct { func (x *TapEvent_Http_StreamId) Reset() { *x = TapEvent_Http_StreamId{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[49] + mi := &file_viz_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3699,7 +3510,7 @@ func (x *TapEvent_Http_StreamId) String() string { func (*TapEvent_Http_StreamId) ProtoMessage() {} func (x *TapEvent_Http_StreamId) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[49] + mi := &file_viz_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3712,7 +3523,7 @@ func (x *TapEvent_Http_StreamId) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_Http_StreamId.ProtoReflect.Descriptor instead. func (*TapEvent_Http_StreamId) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 2, 0} + return file_viz_proto_rawDescGZIP(), []int{13, 2, 0} } func (x *TapEvent_Http_StreamId) GetBase() uint32 { @@ -3745,7 +3556,7 @@ type TapEvent_Http_RequestInit struct { func (x *TapEvent_Http_RequestInit) Reset() { *x = TapEvent_Http_RequestInit{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[50] + mi := &file_viz_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3758,7 +3569,7 @@ func (x *TapEvent_Http_RequestInit) String() string { func (*TapEvent_Http_RequestInit) ProtoMessage() {} func (x *TapEvent_Http_RequestInit) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[50] + mi := &file_viz_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3771,7 +3582,7 @@ func (x *TapEvent_Http_RequestInit) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_Http_RequestInit.ProtoReflect.Descriptor instead. func (*TapEvent_Http_RequestInit) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 2, 1} + return file_viz_proto_rawDescGZIP(), []int{13, 2, 1} } func (x *TapEvent_Http_RequestInit) GetId() *TapEvent_Http_StreamId { @@ -3830,7 +3641,7 @@ type TapEvent_Http_ResponseInit struct { func (x *TapEvent_Http_ResponseInit) Reset() { *x = TapEvent_Http_ResponseInit{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[51] + mi := &file_viz_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3843,7 +3654,7 @@ func (x *TapEvent_Http_ResponseInit) String() string { func (*TapEvent_Http_ResponseInit) ProtoMessage() {} func (x *TapEvent_Http_ResponseInit) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[51] + mi := &file_viz_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3856,7 +3667,7 @@ func (x *TapEvent_Http_ResponseInit) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_Http_ResponseInit.ProtoReflect.Descriptor instead. func (*TapEvent_Http_ResponseInit) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 2, 2} + return file_viz_proto_rawDescGZIP(), []int{13, 2, 2} } func (x *TapEvent_Http_ResponseInit) GetId() *TapEvent_Http_StreamId { @@ -3903,7 +3714,7 @@ type TapEvent_Http_ResponseEnd struct { func (x *TapEvent_Http_ResponseEnd) Reset() { *x = TapEvent_Http_ResponseEnd{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[52] + mi := &file_viz_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3916,7 +3727,7 @@ func (x *TapEvent_Http_ResponseEnd) String() string { func (*TapEvent_Http_ResponseEnd) ProtoMessage() {} func (x *TapEvent_Http_ResponseEnd) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[52] + mi := &file_viz_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3929,7 +3740,7 @@ func (x *TapEvent_Http_ResponseEnd) ProtoReflect() protoreflect.Message { // Deprecated: Use TapEvent_Http_ResponseEnd.ProtoReflect.Descriptor instead. func (*TapEvent_Http_ResponseEnd) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{16, 2, 3} + return file_viz_proto_rawDescGZIP(), []int{13, 2, 3} } func (x *TapEvent_Http_ResponseEnd) GetId() *TapEvent_Http_StreamId { @@ -3987,7 +3798,7 @@ type PodErrors_PodError struct { func (x *PodErrors_PodError) Reset() { *x = PodErrors_PodError{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[53] + mi := &file_viz_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4000,7 +3811,7 @@ func (x *PodErrors_PodError) String() string { func (*PodErrors_PodError) ProtoMessage() {} func (x *PodErrors_PodError) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[53] + mi := &file_viz_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4013,7 +3824,7 @@ func (x *PodErrors_PodError) ProtoReflect() protoreflect.Message { // Deprecated: Use PodErrors_PodError.ProtoReflect.Descriptor instead. func (*PodErrors_PodError) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{18, 0} + return file_viz_proto_rawDescGZIP(), []int{15, 0} } func (m *PodErrors_PodError) GetError() isPodErrors_PodError_Error { @@ -4055,7 +3866,7 @@ type PodErrors_PodError_ContainerError struct { func (x *PodErrors_PodError_ContainerError) Reset() { *x = PodErrors_PodError_ContainerError{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[54] + mi := &file_viz_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4068,7 +3879,7 @@ func (x *PodErrors_PodError_ContainerError) String() string { func (*PodErrors_PodError_ContainerError) ProtoMessage() {} func (x *PodErrors_PodError_ContainerError) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[54] + mi := &file_viz_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4081,7 +3892,7 @@ func (x *PodErrors_PodError_ContainerError) ProtoReflect() protoreflect.Message // Deprecated: Use PodErrors_PodError_ContainerError.ProtoReflect.Descriptor instead. func (*PodErrors_PodError_ContainerError) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{18, 0, 0} + return file_viz_proto_rawDescGZIP(), []int{15, 0, 0} } func (x *PodErrors_PodError_ContainerError) GetMessage() string { @@ -4123,7 +3934,7 @@ type StatSummaryResponse_Ok struct { func (x *StatSummaryResponse_Ok) Reset() { *x = StatSummaryResponse_Ok{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[55] + mi := &file_viz_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4136,7 +3947,7 @@ func (x *StatSummaryResponse_Ok) String() string { func (*StatSummaryResponse_Ok) ProtoMessage() {} func (x *StatSummaryResponse_Ok) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[55] + mi := &file_viz_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4149,7 +3960,7 @@ func (x *StatSummaryResponse_Ok) ProtoReflect() protoreflect.Message { // Deprecated: Use StatSummaryResponse_Ok.ProtoReflect.Descriptor instead. func (*StatSummaryResponse_Ok) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{23, 0} + return file_viz_proto_rawDescGZIP(), []int{20, 0} } func (x *StatSummaryResponse_Ok) GetStatTables() []*StatTable { @@ -4170,7 +3981,7 @@ type StatTable_PodGroup struct { func (x *StatTable_PodGroup) Reset() { *x = StatTable_PodGroup{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[56] + mi := &file_viz_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4183,7 +3994,7 @@ func (x *StatTable_PodGroup) String() string { func (*StatTable_PodGroup) ProtoMessage() {} func (x *StatTable_PodGroup) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[56] + mi := &file_viz_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4196,7 +4007,7 @@ func (x *StatTable_PodGroup) ProtoReflect() protoreflect.Message { // Deprecated: Use StatTable_PodGroup.ProtoReflect.Descriptor instead. func (*StatTable_PodGroup) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{27, 0} + return file_viz_proto_rawDescGZIP(), []int{24, 0} } func (x *StatTable_PodGroup) GetRows() []*StatTable_PodGroup_Row { @@ -4231,7 +4042,7 @@ type StatTable_PodGroup_Row struct { func (x *StatTable_PodGroup_Row) Reset() { *x = StatTable_PodGroup_Row{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[57] + mi := &file_viz_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4244,7 +4055,7 @@ func (x *StatTable_PodGroup_Row) String() string { func (*StatTable_PodGroup_Row) ProtoMessage() {} func (x *StatTable_PodGroup_Row) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[57] + mi := &file_viz_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4257,7 +4068,7 @@ func (x *StatTable_PodGroup_Row) ProtoReflect() protoreflect.Message { // Deprecated: Use StatTable_PodGroup_Row.ProtoReflect.Descriptor instead. func (*StatTable_PodGroup_Row) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{27, 0, 0} + return file_viz_proto_rawDescGZIP(), []int{24, 0, 0} } func (x *StatTable_PodGroup_Row) GetResource() *Resource { @@ -4341,7 +4152,7 @@ type EdgesResponse_Ok struct { func (x *EdgesResponse_Ok) Reset() { *x = EdgesResponse_Ok{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[59] + mi := &file_viz_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4354,7 +4165,7 @@ func (x *EdgesResponse_Ok) String() string { func (*EdgesResponse_Ok) ProtoMessage() {} func (x *EdgesResponse_Ok) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[59] + mi := &file_viz_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4367,7 +4178,7 @@ func (x *EdgesResponse_Ok) ProtoReflect() protoreflect.Message { // Deprecated: Use EdgesResponse_Ok.ProtoReflect.Descriptor instead. func (*EdgesResponse_Ok) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{29, 0} + return file_viz_proto_rawDescGZIP(), []int{26, 0} } func (x *EdgesResponse_Ok) GetEdges() []*Edge { @@ -4388,7 +4199,7 @@ type TopRoutesResponse_Ok struct { func (x *TopRoutesResponse_Ok) Reset() { *x = TopRoutesResponse_Ok{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[60] + mi := &file_viz_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4401,7 +4212,7 @@ func (x *TopRoutesResponse_Ok) String() string { func (*TopRoutesResponse_Ok) ProtoMessage() {} func (x *TopRoutesResponse_Ok) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[60] + mi := &file_viz_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4414,7 +4225,7 @@ func (x *TopRoutesResponse_Ok) ProtoReflect() protoreflect.Message { // Deprecated: Use TopRoutesResponse_Ok.ProtoReflect.Descriptor instead. func (*TopRoutesResponse_Ok) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{32, 0} + return file_viz_proto_rawDescGZIP(), []int{29, 0} } func (x *TopRoutesResponse_Ok) GetRoutes() []*RouteTable { @@ -4438,7 +4249,7 @@ type RouteTable_Row struct { func (x *RouteTable_Row) Reset() { *x = RouteTable_Row{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[61] + mi := &file_viz_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4451,7 +4262,7 @@ func (x *RouteTable_Row) String() string { func (*RouteTable_Row) ProtoMessage() {} func (x *RouteTable_Row) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[61] + mi := &file_viz_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4464,7 +4275,7 @@ func (x *RouteTable_Row) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteTable_Row.ProtoReflect.Descriptor instead. func (*RouteTable_Row) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{33, 0} + return file_viz_proto_rawDescGZIP(), []int{30, 0} } func (x *RouteTable_Row) GetRoute() string { @@ -4513,7 +4324,7 @@ type GatewaysTable_Row struct { func (x *GatewaysTable_Row) Reset() { *x = GatewaysTable_Row{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[62] + mi := &file_viz_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4526,7 +4337,7 @@ func (x *GatewaysTable_Row) String() string { func (*GatewaysTable_Row) ProtoMessage() {} func (x *GatewaysTable_Row) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[62] + mi := &file_viz_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4539,7 +4350,7 @@ func (x *GatewaysTable_Row) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewaysTable_Row.ProtoReflect.Descriptor instead. func (*GatewaysTable_Row) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{34, 0} + return file_viz_proto_rawDescGZIP(), []int{31, 0} } func (x *GatewaysTable_Row) GetNamespace() string { @@ -4609,7 +4420,7 @@ type GatewaysResponse_Ok struct { func (x *GatewaysResponse_Ok) Reset() { *x = GatewaysResponse_Ok{} if protoimpl.UnsafeEnabled { - mi := &file_viz_proto_msgTypes[63] + mi := &file_viz_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4622,7 +4433,7 @@ func (x *GatewaysResponse_Ok) String() string { func (*GatewaysResponse_Ok) ProtoMessage() {} func (x *GatewaysResponse_Ok) ProtoReflect() protoreflect.Message { - mi := &file_viz_proto_msgTypes[63] + mi := &file_viz_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4635,7 +4446,7 @@ func (x *GatewaysResponse_Ok) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewaysResponse_Ok.ProtoReflect.Descriptor instead. func (*GatewaysResponse_Ok) Descriptor() ([]byte, []int) { - return file_viz_proto_rawDescGZIP(), []int{36, 0} + return file_viz_proto_rawDescGZIP(), []int{33, 0} } func (x *GatewaysResponse_Ok) GetGatewaysTable() *GatewaysTable { @@ -4651,657 +4462,638 @@ var file_viz_proto_rawDesc = []byte{ 0x0a, 0x09, 0x76, 0x69, 0x7a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 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, 0x1a, 0x11, 0x68, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, - 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x08, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x22, 0x70, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x22, 0x39, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x22, - 0xfa, 0x04, 0x0a, 0x03, 0x50, 0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, - 0x6f, 0x64, 0x49, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6f, 0x64, 0x49, - 0x50, 0x12, 0x20, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, - 0x65, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x12, 0x37, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, - 0x23, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, - 0x6c, 0x53, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x73, - 0x65, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x63, 0x65, - 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 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, 0x0f, 0x73, 0x69, 0x6e, - 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x13, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x22, - 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, - 0x6e, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 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, 0x06, 0x75, - 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, - 0x61, 0x64, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x22, 0xaa, 0x02, 0x0a, - 0x0a, 0x54, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x70, - 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x70, 0x6f, 0x64, 0x12, - 0x20, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x70, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x02, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x50, - 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x74, 0x6f, 0x50, 0x6f, 0x72, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x49, 0x50, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x6f, 0x49, 0x50, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x72, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x72, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x72, 0x6f, 0x6d, 0x49, 0x50, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x66, 0x72, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, - 0x65, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, - 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, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x3a, 0x02, 0x18, 0x01, 0x42, - 0x08, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xe5, 0x07, 0x0a, 0x14, 0x54, 0x61, - 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x33, + 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3b, + 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x70, 0x0a, 0x0f, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, + 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x3e, 0x0a, 0x05, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6d, - 0x61, 0x78, 0x52, 0x70, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x06, 0x6d, 0x61, 0x78, - 0x52, 0x70, 0x73, 0x12, 0x44, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, - 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x1a, 0xa4, 0x04, 0x0a, 0x05, 0x4d, 0x61, - 0x74, 0x63, 0x68, 0x12, 0x40, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, - 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x53, 0x65, 0x71, 0x48, 0x00, - 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x40, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x53, 0x65, 0x71, - 0x48, 0x00, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x12, 0x3c, 0x0a, 0x03, 0x6e, 0x6f, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, - 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x48, 0x00, - 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, + 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x39, 0x0a, + 0x10, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x25, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, + 0x6f, 0x64, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x22, 0xfa, 0x04, 0x0a, 0x03, 0x50, 0x6f, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x64, 0x49, 0x50, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6f, 0x64, 0x49, 0x50, 0x12, 0x20, 0x0a, 0x0a, 0x64, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0b, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x12, + 0x37, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x66, 0x75, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, 0x12, 0x1f, 0x0a, + 0x0a, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x09, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x12, 0x12, + 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x6a, + 0x6f, 0x62, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, + 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, + 0x12, 0x43, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x06, 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, 0x0f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x75, + 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 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, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x61, 0x64, 0x79, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x22, + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x22, 0xaa, 0x02, 0x0a, 0x0a, 0x54, 0x61, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x70, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x03, 0x70, 0x6f, 0x64, 0x12, 0x20, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, + 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, + 0x78, 0x52, 0x70, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, + 0x70, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x74, 0x6f, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, + 0x49, 0x50, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x6f, 0x49, 0x50, 0x12, 0x1a, + 0x0a, 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x72, + 0x6f, 0x6d, 0x49, 0x50, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x72, 0x6f, 0x6d, + 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 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, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x3a, 0x02, 0x18, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x22, 0xe5, 0x07, 0x0a, 0x14, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, - 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x04, - 0x68, 0x74, 0x74, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, - 0x61, 0x74, 0x63, 0x68, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x48, 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, - 0x70, 0x1a, 0x49, 0x0a, 0x03, 0x53, 0x65, 0x71, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x12, 0x3e, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, + 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x05, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x70, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x70, 0x73, 0x12, 0x44, 0x0a, 0x07, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, + 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x1a, 0xa4, 0x04, 0x0a, 0x05, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x40, 0x0a, 0x03, + 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, - 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, 0x79, 0x0a, 0x04, - 0x48, 0x74, 0x74, 0x70, 0x12, 0x18, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x18, - 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x07, - 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x42, 0x07, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x1a, 0xce, 0x01, 0x0a, 0x07, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x45, 0x0a, 0x04, - 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, - 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x48, 0x00, 0x52, 0x04, 0x68, - 0x74, 0x74, 0x70, 0x1a, 0x71, 0x0a, 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x53, 0x0a, 0x07, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x1a, 0x09, 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x65, - 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, - 0x74, 0x22, 0xf1, 0x01, 0x0a, 0x0a, 0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x12, 0x45, 0x0a, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, - 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0c, 0x75, 0x6e, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x0c, 0x75, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x22, 0x6e, 0x0a, - 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x07, 0x0a, 0x03, 0x47, - 0x45, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x12, 0x07, - 0x0a, 0x03, 0x50, 0x55, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, - 0x45, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x54, 0x43, 0x48, 0x10, 0x04, 0x12, 0x0b, - 0x0a, 0x07, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x43, - 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x45, 0x41, 0x44, - 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x08, 0x42, 0x06, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, - 0x12, 0x41, 0x0a, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, - 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0c, 0x75, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x75, 0x6e, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x22, 0x21, 0x0a, 0x0a, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, - 0x00, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x36, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, - 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, - 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x63, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, - 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x53, 0x74, 0x72, 0x12, 0x1d, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x62, - 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x42, 0x69, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x51, 0x0a, - 0x09, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, - 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x07, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, - 0x12, 0x28, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x49, 0x50, - 0x76, 0x36, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x42, 0x04, 0x0a, 0x02, 0x69, 0x70, - 0x22, 0x30, 0x0a, 0x04, 0x49, 0x50, 0x76, 0x36, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x06, 0x52, 0x05, 0x66, 0x69, 0x72, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x06, 0x52, 0x04, 0x6c, 0x61, - 0x73, 0x74, 0x22, 0x49, 0x0a, 0x0a, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x27, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x49, 0x50, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x64, 0x0a, - 0x03, 0x45, 0x6f, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, - 0x52, 0x0e, 0x67, 0x72, 0x70, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, - 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x65, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, - 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, - 0x73, 0x65, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x05, 0x0a, 0x03, - 0x65, 0x6e, 0x64, 0x22, 0xb4, 0x0f, 0x0a, 0x08, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x30, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, + 0x74, 0x63, 0x68, 0x2e, 0x53, 0x65, 0x71, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x40, + 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x69, + 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x53, 0x65, 0x71, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6e, 0x79, + 0x12, 0x3c, 0x0a, 0x03, 0x6e, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, + 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x48, 0x00, 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x12, 0x45, + 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, + 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, + 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x48, 0x74, + 0x74, 0x70, 0x48, 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x49, 0x0a, 0x03, 0x53, 0x65, + 0x71, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, 0x79, 0x0a, 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x18, 0x0a, + 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x12, 0x1e, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x07, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x42, 0x07, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0xce, 0x01, 0x0a, 0x07, 0x45, 0x78, + 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x45, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, + 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2e, + 0x48, 0x74, 0x74, 0x70, 0x48, 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x71, 0x0a, 0x04, + 0x48, 0x74, 0x74, 0x70, 0x12, 0x53, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, + 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x00, + 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x09, 0x0a, 0x07, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x42, + 0x09, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x22, 0xf1, 0x01, 0x0a, 0x0a, 0x48, + 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x45, 0x0a, 0x0a, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x74, 0x74, + 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x12, 0x24, 0x0a, 0x0c, 0x75, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x75, 0x6e, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x22, 0x6e, 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x65, 0x64, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x45, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, + 0x04, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x55, 0x54, 0x10, 0x02, + 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, + 0x50, 0x41, 0x54, 0x43, 0x48, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x50, 0x54, 0x49, 0x4f, + 0x4e, 0x53, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, + 0x06, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x45, 0x41, 0x44, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x54, + 0x52, 0x41, 0x43, 0x45, 0x10, 0x08, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x9c, + 0x01, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x48, 0x00, + 0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0c, + 0x75, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x75, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x22, 0x21, 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, + 0x54, 0x50, 0x53, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xa6, 0x01, + 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x36, 0x0a, 0x07, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x1a, 0x63, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1d, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x74, 0x72, 0x12, 0x1d, + 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x00, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x69, 0x6e, 0x42, 0x07, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x64, 0x0a, 0x03, 0x45, 0x6f, 0x73, 0x12, 0x2a, 0x0a, + 0x10, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x72, 0x70, 0x63, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x65, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x74, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xc2, 0x0f, 0x0a, + 0x08, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x63, 0x70, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, - 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, - 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x4d, 0x65, 0x74, 0x61, 0x12, 0x3f, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x65, - 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, - 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, - 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x44, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x44, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, - 0x48, 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x92, 0x01, 0x0a, 0x0c, 0x45, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x06, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 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, 0x1a, 0x8c, 0x01, - 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 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, 0x1a, 0xf8, 0x08, 0x0a, - 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x4c, 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, - 0x6e, 0x69, 0x74, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, - 0x69, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x49, 0x6e, 0x69, 0x74, 0x12, 0x4c, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x69, 0x6e, + 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x10, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, + 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x3f, 0x0a, 0x0a, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, + 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x0f, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, + 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x04, + 0x68, 0x74, 0x74, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x45, - 0x6e, 0x64, 0x1a, 0x36, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x61, - 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x1a, 0x86, 0x02, 0x0a, 0x0b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, - 0x74, 0x74, 0x70, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x30, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, - 0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x12, 0x2f, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x1a, 0xdf, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x49, 0x6e, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, - 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x69, - 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x69, 0x74, - 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, 0x10, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, - 0x6e, 0x69, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, - 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0xd6, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x34, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, - 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x12, 0x73, - 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x69, - 0x74, 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, 0x10, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x6e, 0x69, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x03, 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, 0x73, 0x69, - 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, - 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x03, 0x65, 0x6f, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x45, 0x6f, 0x73, 0x52, 0x03, 0x65, 0x6f, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x74, - 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x08, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x42, 0x07, - 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x38, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x42, 0x4f, 0x55, 0x4e, - 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4f, 0x55, 0x54, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, - 0x02, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x08, 0x41, 0x70, - 0x69, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xa4, 0x02, 0x0a, - 0x09, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x06, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x73, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x73, 0x1a, 0xdc, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x4f, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, - 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x50, 0x6f, - 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x1a, 0x76, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, - 0x72, 0x72, 0x6f, 0x72, 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, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, - 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x22, 0x50, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x6e, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, - 0x0a, 0x0e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x59, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0xdf, 0x02, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, - 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, - 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, - 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, - 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, - 0x0a, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x66, - 0x72, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x66, 0x72, - 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6b, - 0x69, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x73, 0x6b, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x63, - 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x22, 0xce, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x02, 0x6f, 0x6b, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, - 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x3e, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x38, 0x0a, - 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x0a, 0x73, 0x74, 0x61, - 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xac, 0x02, 0x0a, 0x0a, 0x42, 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, 0x61, - 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, - 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, - 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x35, 0x30, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, - 0x35, 0x30, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, - 0x5f, 0x70, 0x39, 0x35, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x39, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x39, 0x12, 0x30, - 0x0a, 0x14, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x61, 0x63, - 0x74, 0x75, 0x61, 0x6c, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x30, 0x0a, 0x14, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, - 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x08, 0x54, 0x63, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, - 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, - 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x54, - 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, - 0x22, 0x53, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x53, 0x70, 0x6c, 0x69, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x70, 0x65, 0x78, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x70, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, - 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x12, 0x16, 0x0a, - 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xe6, 0x05, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, - 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x00, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x8e, 0x05, 0x0a, 0x08, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x38, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x1a, 0xc7, 0x04, 0x0a, 0x03, - 0x52, 0x6f, 0x77, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, - 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, - 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, - 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x68, - 0x65, 0x64, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x75, - 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, - 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x42, - 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, - 0x12, 0x33, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x08, 0x20, + 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x48, 0x00, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, + 0x92, 0x01, 0x0a, 0x0c, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x47, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, + 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 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, 0x1a, 0x8c, 0x01, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x12, 0x44, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 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, 0x1a, 0xf8, 0x08, 0x0a, 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x4c, 0x0a, 0x0c, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, + 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x4c, 0x0a, 0x0c, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, + 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x45, 0x6e, 0x64, 0x1a, 0x36, 0x0a, 0x08, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x1a, 0x86, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x69, + 0x74, 0x12, 0x34, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x52, + 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2f, 0x0a, 0x07, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0xdf, 0x01, 0x0a, 0x0c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, + 0x48, 0x74, 0x74, 0x70, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x69, 0x74, 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, 0x10, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, + 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, 0x07, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0xd6, 0x02, 0x0a, + 0x0b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x34, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x69, 0x74, 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, 0x10, 0x73, 0x69, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x6e, + 0x69, 0x74, 0x18, 0x03, 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, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, + 0x03, 0x65, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x6f, 0x73, 0x52, 0x03, 0x65, + 0x6f, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, + 0x76, 0x69, 0x7a, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x08, 0x74, 0x72, 0x61, + 0x69, 0x6c, 0x65, 0x72, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x38, + 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x49, 0x4e, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4f, 0x55, + 0x54, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x22, 0x20, 0x0a, 0x08, 0x41, 0x70, 0x69, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x22, 0xa4, 0x02, 0x0a, 0x09, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x73, 0x12, 0x38, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, + 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0xdc, 0x01, 0x0a, 0x08, + 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4f, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6c, 0x69, + 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x1a, 0x76, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x72, 0x72, 0x6f, 0x72, 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, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x50, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x6e, 0x0a, 0x11, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x54, 0x63, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x08, 0x74, 0x63, 0x70, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x74, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, - 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x53, 0x70, - 0x6c, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x07, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x12, 0x59, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, - 0x6f, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x2e, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79, 0x50, 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79, 0x50, 0x6f, 0x64, 0x1a, 0x57, 0x0a, 0x10, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79, 0x50, 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, - 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x4b, - 0x0a, 0x0c, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, + 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x59, 0x0a, 0x0d, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x32, 0x0a, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xdf, 0x02, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0xb2, 0x01, 0x0a, 0x0d, - 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, - 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, - 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x2e, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, - 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, - 0x64, 0x67, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xbc, 0x01, 0x0a, 0x04, 0x45, 0x64, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x73, 0x72, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, - 0x73, 0x72, 0x63, 0x12, 0x28, 0x0a, 0x03, 0x64, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x64, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x5f, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x6e, 0x6f, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x73, 0x67, 0x22, - 0xe2, 0x01, 0x0a, 0x10, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, - 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x39, 0x0a, - 0x0b, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x22, 0xc2, 0x01, 0x0a, 0x11, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x04, + 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, + 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x5f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x34, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, - 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x1a, 0x36, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x30, 0x0a, 0x06, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x0a, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe7, 0x01, 0x0a, 0x0a, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x8a, 0x01, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x14, - 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, - 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, - 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x65, 0x48, 0x00, 0x52, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x63, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x0a, 0x0a, + 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0xce, 0x01, 0x0a, 0x13, 0x53, 0x74, + 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x36, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x3e, + 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x38, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x42, 0x0a, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xac, 0x02, 0x0a, 0x0a, 0x42, + 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, + 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, + 0x73, 0x5f, 0x70, 0x35, 0x30, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x35, 0x30, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x35, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x35, 0x12, + 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, + 0x39, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x4d, 0x73, 0x50, 0x39, 0x39, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x12, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x63, 0x74, 0x75, 0x61, + 0x6c, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x08, 0x54, 0x63, + 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x65, 0x61, + 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x53, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x63, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x61, 0x70, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x70, 0x65, 0x78, + 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6c, 0x65, 0x61, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xe6, 0x05, 0x0a, + 0x09, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x70, 0x6f, + 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, + 0x00, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x8e, 0x05, 0x0a, 0x08, + 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, + 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, + 0x77, 0x73, 0x1a, 0xc7, 0x04, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x68, 0x65, + 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x64, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x72, 0x75, + 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, + 0x10, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, + 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, + 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x73, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x63, 0x70, 0x53, 0x74, 0x61, + 0x74, 0x73, 0x52, 0x08, 0x74, 0x63, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, + 0x74, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x72, + 0x61, 0x66, 0x66, 0x69, 0x63, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, + 0x07, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x59, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x35, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79, 0x50, 0x6f, + 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79, + 0x50, 0x6f, 0x64, 0x1a, 0x57, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79, 0x50, + 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x07, 0x0a, 0x05, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x4b, 0x0a, 0x0c, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x22, 0xb2, 0x01, 0x0a, 0x0d, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, + 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, + 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, + 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x2e, 0x0a, 0x02, 0x4f, + 0x6b, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, + 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x01, 0x0a, 0x04, 0x45, 0x64, 0x67, 0x65, + 0x12, 0x28, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x28, 0x0a, 0x03, 0x64, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x03, 0x64, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x26, + 0x0a, 0x0f, 0x6e, 0x6f, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x73, + 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x4d, 0x73, 0x67, 0x22, 0xe2, 0x01, 0x0a, 0x10, 0x54, 0x6f, 0x70, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, + 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x04, + 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, + 0x0a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0xc2, 0x01, 0x0a, 0x11, + 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x34, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x1a, 0x36, 0x0a, 0x02, + 0x4f, 0x6b, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x0d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x1a, 0x8b, 0x02, 0x0a, 0x03, 0x52, - 0x6f, 0x77, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x69, 0x72, 0x65, - 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0e, 0x70, 0x61, 0x69, 0x72, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x35, 0x30, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, - 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x35, 0x30, 0x12, 0x24, 0x0a, 0x0e, - 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x35, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, - 0x39, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, - 0x5f, 0x70, 0x39, 0x39, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x39, 0x22, 0x8f, 0x01, 0x0a, 0x0f, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x13, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, - 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x22, 0xd2, 0x01, 0x0a, 0x10, 0x47, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x33, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, - 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, - 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x48, 0x0a, 0x02, 0x4f, 0x6b, 0x12, - 0x42, 0x0a, 0x0e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, - 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x0d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, - 0xde, 0x05, 0x0a, 0x03, 0x41, 0x70, 0x69, 0x12, 0x54, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, - 0x05, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, - 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, - 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x1d, 0x2e, - 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, - 0x0a, 0x09, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, - 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, - 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x03, 0x54, 0x61, 0x70, 0x12, 0x18, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, - 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x03, 0x88, - 0x02, 0x01, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, - 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x66, 0x0a, 0x09, 0x53, 0x65, 0x6c, 0x66, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, - 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, - 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x53, 0x65, 0x6c, - 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x32, 0x99, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x70, 0x12, 0x3e, 0x0a, 0x03, 0x54, 0x61, 0x70, 0x12, - 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, - 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x54, 0x61, 0x70, 0x42, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x42, 0x35, 0x5a, 0x33, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x72, 0x64, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2f, 0x76, 0x69, 0x7a, 0x2f, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x76, 0x69, 0x7a, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x7a, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0xe7, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x30, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x8a, 0x01, + 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x1c, 0x0a, 0x09, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x0d, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x33, 0x0a, 0x04, + 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, + 0x73, 0x1a, 0x8b, 0x02, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, + 0x0a, 0x0f, 0x70, 0x61, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x70, 0x61, 0x69, 0x72, 0x65, 0x64, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x24, 0x0a, + 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x35, 0x30, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, + 0x50, 0x35, 0x30, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, + 0x73, 0x5f, 0x70, 0x39, 0x35, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x39, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x39, 0x22, + 0x8f, 0x01, 0x0a, 0x0f, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, + 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x22, 0xd2, 0x01, 0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x1a, 0x48, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x42, 0x0a, 0x0e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x0d, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd0, 0x04, 0x0a, 0x03, 0x41, 0x70, 0x69, 0x12, 0x54, + 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x05, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, + 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, + 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, + 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, + 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x09, 0x53, + 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x99, 0x01, 0x0a, 0x03, 0x54, 0x61, + 0x70, 0x12, 0x3e, 0x0a, 0x03, 0x54, 0x61, 0x70, 0x12, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, + 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, + 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, + 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x61, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x03, + 0x88, 0x02, 0x01, 0x30, 0x01, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, + 0x65, 0x72, 0x64, 0x32, 0x2f, 0x76, 0x69, 0x7a, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x76, 0x69, 0x7a, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5317,7 +5109,7 @@ func file_viz_proto_rawDescGZIP() []byte { } var file_viz_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_viz_proto_msgTypes = make([]protoimpl.MessageInfo, 64) +var file_viz_proto_msgTypes = make([]protoimpl.MessageInfo, 61) var file_viz_proto_goTypes = []interface{}{ (HttpMethod_Registered)(0), // 0: linkerd2.viz.HttpMethod.Registered (Scheme_Registered)(0), // 1: linkerd2.viz.Scheme.Registered @@ -5334,172 +5126,164 @@ var file_viz_proto_goTypes = []interface{}{ (*HttpMethod)(nil), // 12: linkerd2.viz.HttpMethod (*Scheme)(nil), // 13: linkerd2.viz.Scheme (*Headers)(nil), // 14: linkerd2.viz.Headers - (*IPAddress)(nil), // 15: linkerd2.viz.IPAddress - (*IPv6)(nil), // 16: linkerd2.viz.IPv6 - (*TcpAddress)(nil), // 17: linkerd2.viz.TcpAddress - (*Eos)(nil), // 18: linkerd2.viz.Eos - (*TapEvent)(nil), // 19: linkerd2.viz.TapEvent - (*ApiError)(nil), // 20: linkerd2.viz.ApiError - (*PodErrors)(nil), // 21: linkerd2.viz.PodErrors - (*Resource)(nil), // 22: linkerd2.viz.Resource - (*ResourceSelection)(nil), // 23: linkerd2.viz.ResourceSelection - (*ResourceError)(nil), // 24: linkerd2.viz.ResourceError - (*StatSummaryRequest)(nil), // 25: linkerd2.viz.StatSummaryRequest - (*StatSummaryResponse)(nil), // 26: linkerd2.viz.StatSummaryResponse - (*BasicStats)(nil), // 27: linkerd2.viz.BasicStats - (*TcpStats)(nil), // 28: linkerd2.viz.TcpStats - (*TrafficSplitStats)(nil), // 29: linkerd2.viz.TrafficSplitStats - (*StatTable)(nil), // 30: linkerd2.viz.StatTable - (*EdgesRequest)(nil), // 31: linkerd2.viz.EdgesRequest - (*EdgesResponse)(nil), // 32: linkerd2.viz.EdgesResponse - (*Edge)(nil), // 33: linkerd2.viz.Edge - (*TopRoutesRequest)(nil), // 34: linkerd2.viz.TopRoutesRequest - (*TopRoutesResponse)(nil), // 35: linkerd2.viz.TopRoutesResponse - (*RouteTable)(nil), // 36: linkerd2.viz.RouteTable - (*GatewaysTable)(nil), // 37: linkerd2.viz.GatewaysTable - (*GatewaysRequest)(nil), // 38: linkerd2.viz.GatewaysRequest - (*GatewaysResponse)(nil), // 39: linkerd2.viz.GatewaysResponse - (*TapByResourceRequest_Match)(nil), // 40: linkerd2.viz.TapByResourceRequest.Match - (*TapByResourceRequest_Extract)(nil), // 41: linkerd2.viz.TapByResourceRequest.Extract - (*TapByResourceRequest_Match_Seq)(nil), // 42: linkerd2.viz.TapByResourceRequest.Match.Seq - (*TapByResourceRequest_Match_Http)(nil), // 43: linkerd2.viz.TapByResourceRequest.Match.Http - (*TapByResourceRequest_Extract_Http)(nil), // 44: linkerd2.viz.TapByResourceRequest.Extract.Http - (*TapByResourceRequest_Extract_Http_Headers)(nil), // 45: linkerd2.viz.TapByResourceRequest.Extract.Http.Headers - (*Headers_Header)(nil), // 46: linkerd2.viz.Headers.Header - (*TapEvent_EndpointMeta)(nil), // 47: linkerd2.viz.TapEvent.EndpointMeta - (*TapEvent_RouteMeta)(nil), // 48: linkerd2.viz.TapEvent.RouteMeta - (*TapEvent_Http)(nil), // 49: linkerd2.viz.TapEvent.Http - nil, // 50: linkerd2.viz.TapEvent.EndpointMeta.LabelsEntry - nil, // 51: linkerd2.viz.TapEvent.RouteMeta.LabelsEntry - (*TapEvent_Http_StreamId)(nil), // 52: linkerd2.viz.TapEvent.Http.StreamId - (*TapEvent_Http_RequestInit)(nil), // 53: linkerd2.viz.TapEvent.Http.RequestInit - (*TapEvent_Http_ResponseInit)(nil), // 54: linkerd2.viz.TapEvent.Http.ResponseInit - (*TapEvent_Http_ResponseEnd)(nil), // 55: linkerd2.viz.TapEvent.Http.ResponseEnd - (*PodErrors_PodError)(nil), // 56: linkerd2.viz.PodErrors.PodError - (*PodErrors_PodError_ContainerError)(nil), // 57: linkerd2.viz.PodErrors.PodError.ContainerError - (*StatSummaryResponse_Ok)(nil), // 58: linkerd2.viz.StatSummaryResponse.Ok - (*StatTable_PodGroup)(nil), // 59: linkerd2.viz.StatTable.PodGroup - (*StatTable_PodGroup_Row)(nil), // 60: linkerd2.viz.StatTable.PodGroup.Row - nil, // 61: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry - (*EdgesResponse_Ok)(nil), // 62: linkerd2.viz.EdgesResponse.Ok - (*TopRoutesResponse_Ok)(nil), // 63: linkerd2.viz.TopRoutesResponse.Ok - (*RouteTable_Row)(nil), // 64: linkerd2.viz.RouteTable.Row - (*GatewaysTable_Row)(nil), // 65: linkerd2.viz.GatewaysTable.Row - (*GatewaysResponse_Ok)(nil), // 66: linkerd2.viz.GatewaysResponse.Ok - (*duration.Duration)(nil), // 67: google.protobuf.Duration - (*healthcheck.SelfCheckRequest)(nil), // 68: linkerd2.viz.healthcheck.SelfCheckRequest - (*healthcheck.SelfCheckResponse)(nil), // 69: linkerd2.viz.healthcheck.SelfCheckResponse + (*Eos)(nil), // 15: linkerd2.viz.Eos + (*TapEvent)(nil), // 16: linkerd2.viz.TapEvent + (*ApiError)(nil), // 17: linkerd2.viz.ApiError + (*PodErrors)(nil), // 18: linkerd2.viz.PodErrors + (*Resource)(nil), // 19: linkerd2.viz.Resource + (*ResourceSelection)(nil), // 20: linkerd2.viz.ResourceSelection + (*ResourceError)(nil), // 21: linkerd2.viz.ResourceError + (*StatSummaryRequest)(nil), // 22: linkerd2.viz.StatSummaryRequest + (*StatSummaryResponse)(nil), // 23: linkerd2.viz.StatSummaryResponse + (*BasicStats)(nil), // 24: linkerd2.viz.BasicStats + (*TcpStats)(nil), // 25: linkerd2.viz.TcpStats + (*TrafficSplitStats)(nil), // 26: linkerd2.viz.TrafficSplitStats + (*StatTable)(nil), // 27: linkerd2.viz.StatTable + (*EdgesRequest)(nil), // 28: linkerd2.viz.EdgesRequest + (*EdgesResponse)(nil), // 29: linkerd2.viz.EdgesResponse + (*Edge)(nil), // 30: linkerd2.viz.Edge + (*TopRoutesRequest)(nil), // 31: linkerd2.viz.TopRoutesRequest + (*TopRoutesResponse)(nil), // 32: linkerd2.viz.TopRoutesResponse + (*RouteTable)(nil), // 33: linkerd2.viz.RouteTable + (*GatewaysTable)(nil), // 34: linkerd2.viz.GatewaysTable + (*GatewaysRequest)(nil), // 35: linkerd2.viz.GatewaysRequest + (*GatewaysResponse)(nil), // 36: linkerd2.viz.GatewaysResponse + (*TapByResourceRequest_Match)(nil), // 37: linkerd2.viz.TapByResourceRequest.Match + (*TapByResourceRequest_Extract)(nil), // 38: linkerd2.viz.TapByResourceRequest.Extract + (*TapByResourceRequest_Match_Seq)(nil), // 39: linkerd2.viz.TapByResourceRequest.Match.Seq + (*TapByResourceRequest_Match_Http)(nil), // 40: linkerd2.viz.TapByResourceRequest.Match.Http + (*TapByResourceRequest_Extract_Http)(nil), // 41: linkerd2.viz.TapByResourceRequest.Extract.Http + (*TapByResourceRequest_Extract_Http_Headers)(nil), // 42: linkerd2.viz.TapByResourceRequest.Extract.Http.Headers + (*Headers_Header)(nil), // 43: linkerd2.viz.Headers.Header + (*TapEvent_EndpointMeta)(nil), // 44: linkerd2.viz.TapEvent.EndpointMeta + (*TapEvent_RouteMeta)(nil), // 45: linkerd2.viz.TapEvent.RouteMeta + (*TapEvent_Http)(nil), // 46: linkerd2.viz.TapEvent.Http + nil, // 47: linkerd2.viz.TapEvent.EndpointMeta.LabelsEntry + nil, // 48: linkerd2.viz.TapEvent.RouteMeta.LabelsEntry + (*TapEvent_Http_StreamId)(nil), // 49: linkerd2.viz.TapEvent.Http.StreamId + (*TapEvent_Http_RequestInit)(nil), // 50: linkerd2.viz.TapEvent.Http.RequestInit + (*TapEvent_Http_ResponseInit)(nil), // 51: linkerd2.viz.TapEvent.Http.ResponseInit + (*TapEvent_Http_ResponseEnd)(nil), // 52: linkerd2.viz.TapEvent.Http.ResponseEnd + (*PodErrors_PodError)(nil), // 53: linkerd2.viz.PodErrors.PodError + (*PodErrors_PodError_ContainerError)(nil), // 54: linkerd2.viz.PodErrors.PodError.ContainerError + (*StatSummaryResponse_Ok)(nil), // 55: linkerd2.viz.StatSummaryResponse.Ok + (*StatTable_PodGroup)(nil), // 56: linkerd2.viz.StatTable.PodGroup + (*StatTable_PodGroup_Row)(nil), // 57: linkerd2.viz.StatTable.PodGroup.Row + nil, // 58: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry + (*EdgesResponse_Ok)(nil), // 59: linkerd2.viz.EdgesResponse.Ok + (*TopRoutesResponse_Ok)(nil), // 60: linkerd2.viz.TopRoutesResponse.Ok + (*RouteTable_Row)(nil), // 61: linkerd2.viz.RouteTable.Row + (*GatewaysTable_Row)(nil), // 62: linkerd2.viz.GatewaysTable.Row + (*GatewaysResponse_Ok)(nil), // 63: linkerd2.viz.GatewaysResponse.Ok + (*duration.Duration)(nil), // 64: google.protobuf.Duration + (*net.TcpAddress)(nil), // 65: linkerd2.common.net.TcpAddress + (*healthcheck.SelfCheckRequest)(nil), // 66: linkerd2.common.healthcheck.SelfCheckRequest + (*healthcheck.SelfCheckResponse)(nil), // 67: linkerd2.common.healthcheck.SelfCheckResponse } var file_viz_proto_depIdxs = []int32{ 6, // 0: linkerd2.viz.ListServicesResponse.services:type_name -> linkerd2.viz.Service - 23, // 1: linkerd2.viz.ListPodsRequest.selector:type_name -> linkerd2.viz.ResourceSelection + 20, // 1: linkerd2.viz.ListPodsRequest.selector:type_name -> linkerd2.viz.ResourceSelection 9, // 2: linkerd2.viz.ListPodsResponse.pods:type_name -> linkerd2.viz.Pod - 67, // 3: linkerd2.viz.Pod.sinceLastReport:type_name -> google.protobuf.Duration - 67, // 4: linkerd2.viz.Pod.uptime:type_name -> google.protobuf.Duration - 23, // 5: linkerd2.viz.TapByResourceRequest.target:type_name -> linkerd2.viz.ResourceSelection - 40, // 6: linkerd2.viz.TapByResourceRequest.match:type_name -> linkerd2.viz.TapByResourceRequest.Match - 41, // 7: linkerd2.viz.TapByResourceRequest.extract:type_name -> linkerd2.viz.TapByResourceRequest.Extract + 64, // 3: linkerd2.viz.Pod.sinceLastReport:type_name -> google.protobuf.Duration + 64, // 4: linkerd2.viz.Pod.uptime:type_name -> google.protobuf.Duration + 20, // 5: linkerd2.viz.TapByResourceRequest.target:type_name -> linkerd2.viz.ResourceSelection + 37, // 6: linkerd2.viz.TapByResourceRequest.match:type_name -> linkerd2.viz.TapByResourceRequest.Match + 38, // 7: linkerd2.viz.TapByResourceRequest.extract:type_name -> linkerd2.viz.TapByResourceRequest.Extract 0, // 8: linkerd2.viz.HttpMethod.registered:type_name -> linkerd2.viz.HttpMethod.Registered 1, // 9: linkerd2.viz.Scheme.registered:type_name -> linkerd2.viz.Scheme.Registered - 46, // 10: linkerd2.viz.Headers.headers:type_name -> linkerd2.viz.Headers.Header - 16, // 11: linkerd2.viz.IPAddress.ipv6:type_name -> linkerd2.viz.IPv6 - 15, // 12: linkerd2.viz.TcpAddress.ip:type_name -> linkerd2.viz.IPAddress - 17, // 13: linkerd2.viz.TapEvent.source:type_name -> linkerd2.viz.TcpAddress - 47, // 14: linkerd2.viz.TapEvent.source_meta:type_name -> linkerd2.viz.TapEvent.EndpointMeta - 17, // 15: linkerd2.viz.TapEvent.destination:type_name -> linkerd2.viz.TcpAddress - 47, // 16: linkerd2.viz.TapEvent.destination_meta:type_name -> linkerd2.viz.TapEvent.EndpointMeta - 48, // 17: linkerd2.viz.TapEvent.route_meta:type_name -> linkerd2.viz.TapEvent.RouteMeta - 2, // 18: linkerd2.viz.TapEvent.proxy_direction:type_name -> linkerd2.viz.TapEvent.ProxyDirection - 49, // 19: linkerd2.viz.TapEvent.http:type_name -> linkerd2.viz.TapEvent.Http - 56, // 20: linkerd2.viz.PodErrors.errors:type_name -> linkerd2.viz.PodErrors.PodError - 22, // 21: linkerd2.viz.ResourceSelection.resource:type_name -> linkerd2.viz.Resource - 22, // 22: linkerd2.viz.ResourceError.resource:type_name -> linkerd2.viz.Resource - 23, // 23: linkerd2.viz.StatSummaryRequest.selector:type_name -> linkerd2.viz.ResourceSelection - 3, // 24: linkerd2.viz.StatSummaryRequest.none:type_name -> linkerd2.viz.Empty - 22, // 25: linkerd2.viz.StatSummaryRequest.to_resource:type_name -> linkerd2.viz.Resource - 22, // 26: linkerd2.viz.StatSummaryRequest.from_resource:type_name -> linkerd2.viz.Resource - 58, // 27: linkerd2.viz.StatSummaryResponse.ok:type_name -> linkerd2.viz.StatSummaryResponse.Ok - 24, // 28: linkerd2.viz.StatSummaryResponse.error:type_name -> linkerd2.viz.ResourceError - 59, // 29: linkerd2.viz.StatTable.pod_group:type_name -> linkerd2.viz.StatTable.PodGroup - 23, // 30: linkerd2.viz.EdgesRequest.selector:type_name -> linkerd2.viz.ResourceSelection - 62, // 31: linkerd2.viz.EdgesResponse.ok:type_name -> linkerd2.viz.EdgesResponse.Ok - 24, // 32: linkerd2.viz.EdgesResponse.error:type_name -> linkerd2.viz.ResourceError - 22, // 33: linkerd2.viz.Edge.src:type_name -> linkerd2.viz.Resource - 22, // 34: linkerd2.viz.Edge.dst:type_name -> linkerd2.viz.Resource - 23, // 35: linkerd2.viz.TopRoutesRequest.selector:type_name -> linkerd2.viz.ResourceSelection - 3, // 36: linkerd2.viz.TopRoutesRequest.none:type_name -> linkerd2.viz.Empty - 22, // 37: linkerd2.viz.TopRoutesRequest.to_resource:type_name -> linkerd2.viz.Resource - 24, // 38: linkerd2.viz.TopRoutesResponse.error:type_name -> linkerd2.viz.ResourceError - 63, // 39: linkerd2.viz.TopRoutesResponse.ok:type_name -> linkerd2.viz.TopRoutesResponse.Ok - 64, // 40: linkerd2.viz.RouteTable.rows:type_name -> linkerd2.viz.RouteTable.Row - 65, // 41: linkerd2.viz.GatewaysTable.rows:type_name -> linkerd2.viz.GatewaysTable.Row - 66, // 42: linkerd2.viz.GatewaysResponse.ok:type_name -> linkerd2.viz.GatewaysResponse.Ok - 24, // 43: linkerd2.viz.GatewaysResponse.error:type_name -> linkerd2.viz.ResourceError - 42, // 44: linkerd2.viz.TapByResourceRequest.Match.all:type_name -> linkerd2.viz.TapByResourceRequest.Match.Seq - 42, // 45: linkerd2.viz.TapByResourceRequest.Match.any:type_name -> linkerd2.viz.TapByResourceRequest.Match.Seq - 40, // 46: linkerd2.viz.TapByResourceRequest.Match.not:type_name -> linkerd2.viz.TapByResourceRequest.Match - 23, // 47: linkerd2.viz.TapByResourceRequest.Match.destinations:type_name -> linkerd2.viz.ResourceSelection - 43, // 48: linkerd2.viz.TapByResourceRequest.Match.http:type_name -> linkerd2.viz.TapByResourceRequest.Match.Http - 44, // 49: linkerd2.viz.TapByResourceRequest.Extract.http:type_name -> linkerd2.viz.TapByResourceRequest.Extract.Http - 40, // 50: linkerd2.viz.TapByResourceRequest.Match.Seq.matches:type_name -> linkerd2.viz.TapByResourceRequest.Match - 45, // 51: linkerd2.viz.TapByResourceRequest.Extract.Http.headers:type_name -> linkerd2.viz.TapByResourceRequest.Extract.Http.Headers - 50, // 52: linkerd2.viz.TapEvent.EndpointMeta.labels:type_name -> linkerd2.viz.TapEvent.EndpointMeta.LabelsEntry - 51, // 53: linkerd2.viz.TapEvent.RouteMeta.labels:type_name -> linkerd2.viz.TapEvent.RouteMeta.LabelsEntry - 53, // 54: linkerd2.viz.TapEvent.Http.request_init:type_name -> linkerd2.viz.TapEvent.Http.RequestInit - 54, // 55: linkerd2.viz.TapEvent.Http.response_init:type_name -> linkerd2.viz.TapEvent.Http.ResponseInit - 55, // 56: linkerd2.viz.TapEvent.Http.response_end:type_name -> linkerd2.viz.TapEvent.Http.ResponseEnd - 52, // 57: linkerd2.viz.TapEvent.Http.RequestInit.id:type_name -> linkerd2.viz.TapEvent.Http.StreamId - 12, // 58: linkerd2.viz.TapEvent.Http.RequestInit.method:type_name -> linkerd2.viz.HttpMethod - 13, // 59: linkerd2.viz.TapEvent.Http.RequestInit.scheme:type_name -> linkerd2.viz.Scheme - 14, // 60: linkerd2.viz.TapEvent.Http.RequestInit.headers:type_name -> linkerd2.viz.Headers - 52, // 61: linkerd2.viz.TapEvent.Http.ResponseInit.id:type_name -> linkerd2.viz.TapEvent.Http.StreamId - 67, // 62: linkerd2.viz.TapEvent.Http.ResponseInit.since_request_init:type_name -> google.protobuf.Duration - 14, // 63: linkerd2.viz.TapEvent.Http.ResponseInit.headers:type_name -> linkerd2.viz.Headers - 52, // 64: linkerd2.viz.TapEvent.Http.ResponseEnd.id:type_name -> linkerd2.viz.TapEvent.Http.StreamId - 67, // 65: linkerd2.viz.TapEvent.Http.ResponseEnd.since_request_init:type_name -> google.protobuf.Duration - 67, // 66: linkerd2.viz.TapEvent.Http.ResponseEnd.since_response_init:type_name -> google.protobuf.Duration - 18, // 67: linkerd2.viz.TapEvent.Http.ResponseEnd.eos:type_name -> linkerd2.viz.Eos - 14, // 68: linkerd2.viz.TapEvent.Http.ResponseEnd.trailers:type_name -> linkerd2.viz.Headers - 57, // 69: linkerd2.viz.PodErrors.PodError.container:type_name -> linkerd2.viz.PodErrors.PodError.ContainerError - 30, // 70: linkerd2.viz.StatSummaryResponse.Ok.stat_tables:type_name -> linkerd2.viz.StatTable - 60, // 71: linkerd2.viz.StatTable.PodGroup.rows:type_name -> linkerd2.viz.StatTable.PodGroup.Row - 22, // 72: linkerd2.viz.StatTable.PodGroup.Row.resource:type_name -> linkerd2.viz.Resource - 27, // 73: linkerd2.viz.StatTable.PodGroup.Row.stats:type_name -> linkerd2.viz.BasicStats - 28, // 74: linkerd2.viz.StatTable.PodGroup.Row.tcp_stats:type_name -> linkerd2.viz.TcpStats - 29, // 75: linkerd2.viz.StatTable.PodGroup.Row.ts_stats:type_name -> linkerd2.viz.TrafficSplitStats - 61, // 76: linkerd2.viz.StatTable.PodGroup.Row.errors_by_pod:type_name -> linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry - 21, // 77: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry.value:type_name -> linkerd2.viz.PodErrors - 33, // 78: linkerd2.viz.EdgesResponse.Ok.edges:type_name -> linkerd2.viz.Edge - 36, // 79: linkerd2.viz.TopRoutesResponse.Ok.routes:type_name -> linkerd2.viz.RouteTable - 27, // 80: linkerd2.viz.RouteTable.Row.stats:type_name -> linkerd2.viz.BasicStats - 37, // 81: linkerd2.viz.GatewaysResponse.Ok.gateways_table:type_name -> linkerd2.viz.GatewaysTable - 25, // 82: linkerd2.viz.Api.StatSummary:input_type -> linkerd2.viz.StatSummaryRequest - 31, // 83: linkerd2.viz.Api.Edges:input_type -> linkerd2.viz.EdgesRequest - 38, // 84: linkerd2.viz.Api.Gateways:input_type -> linkerd2.viz.GatewaysRequest - 34, // 85: linkerd2.viz.Api.TopRoutes:input_type -> linkerd2.viz.TopRoutesRequest - 7, // 86: linkerd2.viz.Api.ListPods:input_type -> linkerd2.viz.ListPodsRequest - 4, // 87: linkerd2.viz.Api.ListServices:input_type -> linkerd2.viz.ListServicesRequest - 10, // 88: linkerd2.viz.Api.Tap:input_type -> linkerd2.viz.TapRequest - 11, // 89: linkerd2.viz.Api.TapByResource:input_type -> linkerd2.viz.TapByResourceRequest - 68, // 90: linkerd2.viz.Api.SelfCheck:input_type -> linkerd2.viz.healthcheck.SelfCheckRequest - 10, // 91: linkerd2.viz.Tap.Tap:input_type -> linkerd2.viz.TapRequest - 11, // 92: linkerd2.viz.Tap.TapByResource:input_type -> linkerd2.viz.TapByResourceRequest - 26, // 93: linkerd2.viz.Api.StatSummary:output_type -> linkerd2.viz.StatSummaryResponse - 32, // 94: linkerd2.viz.Api.Edges:output_type -> linkerd2.viz.EdgesResponse - 39, // 95: linkerd2.viz.Api.Gateways:output_type -> linkerd2.viz.GatewaysResponse - 35, // 96: linkerd2.viz.Api.TopRoutes:output_type -> linkerd2.viz.TopRoutesResponse - 8, // 97: linkerd2.viz.Api.ListPods:output_type -> linkerd2.viz.ListPodsResponse - 5, // 98: linkerd2.viz.Api.ListServices:output_type -> linkerd2.viz.ListServicesResponse - 19, // 99: linkerd2.viz.Api.Tap:output_type -> linkerd2.viz.TapEvent - 19, // 100: linkerd2.viz.Api.TapByResource:output_type -> linkerd2.viz.TapEvent - 69, // 101: linkerd2.viz.Api.SelfCheck:output_type -> linkerd2.viz.healthcheck.SelfCheckResponse - 19, // 102: linkerd2.viz.Tap.Tap:output_type -> linkerd2.viz.TapEvent - 19, // 103: linkerd2.viz.Tap.TapByResource:output_type -> linkerd2.viz.TapEvent - 93, // [93:104] is the sub-list for method output_type - 82, // [82:93] is the sub-list for method input_type - 82, // [82:82] is the sub-list for extension type_name - 82, // [82:82] is the sub-list for extension extendee - 0, // [0:82] is the sub-list for field type_name + 43, // 10: linkerd2.viz.Headers.headers:type_name -> linkerd2.viz.Headers.Header + 65, // 11: linkerd2.viz.TapEvent.source:type_name -> linkerd2.common.net.TcpAddress + 44, // 12: linkerd2.viz.TapEvent.source_meta:type_name -> linkerd2.viz.TapEvent.EndpointMeta + 65, // 13: linkerd2.viz.TapEvent.destination:type_name -> linkerd2.common.net.TcpAddress + 44, // 14: linkerd2.viz.TapEvent.destination_meta:type_name -> linkerd2.viz.TapEvent.EndpointMeta + 45, // 15: linkerd2.viz.TapEvent.route_meta:type_name -> linkerd2.viz.TapEvent.RouteMeta + 2, // 16: linkerd2.viz.TapEvent.proxy_direction:type_name -> linkerd2.viz.TapEvent.ProxyDirection + 46, // 17: linkerd2.viz.TapEvent.http:type_name -> linkerd2.viz.TapEvent.Http + 53, // 18: linkerd2.viz.PodErrors.errors:type_name -> linkerd2.viz.PodErrors.PodError + 19, // 19: linkerd2.viz.ResourceSelection.resource:type_name -> linkerd2.viz.Resource + 19, // 20: linkerd2.viz.ResourceError.resource:type_name -> linkerd2.viz.Resource + 20, // 21: linkerd2.viz.StatSummaryRequest.selector:type_name -> linkerd2.viz.ResourceSelection + 3, // 22: linkerd2.viz.StatSummaryRequest.none:type_name -> linkerd2.viz.Empty + 19, // 23: linkerd2.viz.StatSummaryRequest.to_resource:type_name -> linkerd2.viz.Resource + 19, // 24: linkerd2.viz.StatSummaryRequest.from_resource:type_name -> linkerd2.viz.Resource + 55, // 25: linkerd2.viz.StatSummaryResponse.ok:type_name -> linkerd2.viz.StatSummaryResponse.Ok + 21, // 26: linkerd2.viz.StatSummaryResponse.error:type_name -> linkerd2.viz.ResourceError + 56, // 27: linkerd2.viz.StatTable.pod_group:type_name -> linkerd2.viz.StatTable.PodGroup + 20, // 28: linkerd2.viz.EdgesRequest.selector:type_name -> linkerd2.viz.ResourceSelection + 59, // 29: linkerd2.viz.EdgesResponse.ok:type_name -> linkerd2.viz.EdgesResponse.Ok + 21, // 30: linkerd2.viz.EdgesResponse.error:type_name -> linkerd2.viz.ResourceError + 19, // 31: linkerd2.viz.Edge.src:type_name -> linkerd2.viz.Resource + 19, // 32: linkerd2.viz.Edge.dst:type_name -> linkerd2.viz.Resource + 20, // 33: linkerd2.viz.TopRoutesRequest.selector:type_name -> linkerd2.viz.ResourceSelection + 3, // 34: linkerd2.viz.TopRoutesRequest.none:type_name -> linkerd2.viz.Empty + 19, // 35: linkerd2.viz.TopRoutesRequest.to_resource:type_name -> linkerd2.viz.Resource + 21, // 36: linkerd2.viz.TopRoutesResponse.error:type_name -> linkerd2.viz.ResourceError + 60, // 37: linkerd2.viz.TopRoutesResponse.ok:type_name -> linkerd2.viz.TopRoutesResponse.Ok + 61, // 38: linkerd2.viz.RouteTable.rows:type_name -> linkerd2.viz.RouteTable.Row + 62, // 39: linkerd2.viz.GatewaysTable.rows:type_name -> linkerd2.viz.GatewaysTable.Row + 63, // 40: linkerd2.viz.GatewaysResponse.ok:type_name -> linkerd2.viz.GatewaysResponse.Ok + 21, // 41: linkerd2.viz.GatewaysResponse.error:type_name -> linkerd2.viz.ResourceError + 39, // 42: linkerd2.viz.TapByResourceRequest.Match.all:type_name -> linkerd2.viz.TapByResourceRequest.Match.Seq + 39, // 43: linkerd2.viz.TapByResourceRequest.Match.any:type_name -> linkerd2.viz.TapByResourceRequest.Match.Seq + 37, // 44: linkerd2.viz.TapByResourceRequest.Match.not:type_name -> linkerd2.viz.TapByResourceRequest.Match + 20, // 45: linkerd2.viz.TapByResourceRequest.Match.destinations:type_name -> linkerd2.viz.ResourceSelection + 40, // 46: linkerd2.viz.TapByResourceRequest.Match.http:type_name -> linkerd2.viz.TapByResourceRequest.Match.Http + 41, // 47: linkerd2.viz.TapByResourceRequest.Extract.http:type_name -> linkerd2.viz.TapByResourceRequest.Extract.Http + 37, // 48: linkerd2.viz.TapByResourceRequest.Match.Seq.matches:type_name -> linkerd2.viz.TapByResourceRequest.Match + 42, // 49: linkerd2.viz.TapByResourceRequest.Extract.Http.headers:type_name -> linkerd2.viz.TapByResourceRequest.Extract.Http.Headers + 47, // 50: linkerd2.viz.TapEvent.EndpointMeta.labels:type_name -> linkerd2.viz.TapEvent.EndpointMeta.LabelsEntry + 48, // 51: linkerd2.viz.TapEvent.RouteMeta.labels:type_name -> linkerd2.viz.TapEvent.RouteMeta.LabelsEntry + 50, // 52: linkerd2.viz.TapEvent.Http.request_init:type_name -> linkerd2.viz.TapEvent.Http.RequestInit + 51, // 53: linkerd2.viz.TapEvent.Http.response_init:type_name -> linkerd2.viz.TapEvent.Http.ResponseInit + 52, // 54: linkerd2.viz.TapEvent.Http.response_end:type_name -> linkerd2.viz.TapEvent.Http.ResponseEnd + 49, // 55: linkerd2.viz.TapEvent.Http.RequestInit.id:type_name -> linkerd2.viz.TapEvent.Http.StreamId + 12, // 56: linkerd2.viz.TapEvent.Http.RequestInit.method:type_name -> linkerd2.viz.HttpMethod + 13, // 57: linkerd2.viz.TapEvent.Http.RequestInit.scheme:type_name -> linkerd2.viz.Scheme + 14, // 58: linkerd2.viz.TapEvent.Http.RequestInit.headers:type_name -> linkerd2.viz.Headers + 49, // 59: linkerd2.viz.TapEvent.Http.ResponseInit.id:type_name -> linkerd2.viz.TapEvent.Http.StreamId + 64, // 60: linkerd2.viz.TapEvent.Http.ResponseInit.since_request_init:type_name -> google.protobuf.Duration + 14, // 61: linkerd2.viz.TapEvent.Http.ResponseInit.headers:type_name -> linkerd2.viz.Headers + 49, // 62: linkerd2.viz.TapEvent.Http.ResponseEnd.id:type_name -> linkerd2.viz.TapEvent.Http.StreamId + 64, // 63: linkerd2.viz.TapEvent.Http.ResponseEnd.since_request_init:type_name -> google.protobuf.Duration + 64, // 64: linkerd2.viz.TapEvent.Http.ResponseEnd.since_response_init:type_name -> google.protobuf.Duration + 15, // 65: linkerd2.viz.TapEvent.Http.ResponseEnd.eos:type_name -> linkerd2.viz.Eos + 14, // 66: linkerd2.viz.TapEvent.Http.ResponseEnd.trailers:type_name -> linkerd2.viz.Headers + 54, // 67: linkerd2.viz.PodErrors.PodError.container:type_name -> linkerd2.viz.PodErrors.PodError.ContainerError + 27, // 68: linkerd2.viz.StatSummaryResponse.Ok.stat_tables:type_name -> linkerd2.viz.StatTable + 57, // 69: linkerd2.viz.StatTable.PodGroup.rows:type_name -> linkerd2.viz.StatTable.PodGroup.Row + 19, // 70: linkerd2.viz.StatTable.PodGroup.Row.resource:type_name -> linkerd2.viz.Resource + 24, // 71: linkerd2.viz.StatTable.PodGroup.Row.stats:type_name -> linkerd2.viz.BasicStats + 25, // 72: linkerd2.viz.StatTable.PodGroup.Row.tcp_stats:type_name -> linkerd2.viz.TcpStats + 26, // 73: linkerd2.viz.StatTable.PodGroup.Row.ts_stats:type_name -> linkerd2.viz.TrafficSplitStats + 58, // 74: linkerd2.viz.StatTable.PodGroup.Row.errors_by_pod:type_name -> linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry + 18, // 75: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry.value:type_name -> linkerd2.viz.PodErrors + 30, // 76: linkerd2.viz.EdgesResponse.Ok.edges:type_name -> linkerd2.viz.Edge + 33, // 77: linkerd2.viz.TopRoutesResponse.Ok.routes:type_name -> linkerd2.viz.RouteTable + 24, // 78: linkerd2.viz.RouteTable.Row.stats:type_name -> linkerd2.viz.BasicStats + 34, // 79: linkerd2.viz.GatewaysResponse.Ok.gateways_table:type_name -> linkerd2.viz.GatewaysTable + 22, // 80: linkerd2.viz.Api.StatSummary:input_type -> linkerd2.viz.StatSummaryRequest + 28, // 81: linkerd2.viz.Api.Edges:input_type -> linkerd2.viz.EdgesRequest + 35, // 82: linkerd2.viz.Api.Gateways:input_type -> linkerd2.viz.GatewaysRequest + 31, // 83: linkerd2.viz.Api.TopRoutes:input_type -> linkerd2.viz.TopRoutesRequest + 7, // 84: linkerd2.viz.Api.ListPods:input_type -> linkerd2.viz.ListPodsRequest + 4, // 85: linkerd2.viz.Api.ListServices:input_type -> linkerd2.viz.ListServicesRequest + 66, // 86: linkerd2.viz.Api.SelfCheck:input_type -> linkerd2.common.healthcheck.SelfCheckRequest + 10, // 87: linkerd2.viz.Tap.Tap:input_type -> linkerd2.viz.TapRequest + 11, // 88: linkerd2.viz.Tap.TapByResource:input_type -> linkerd2.viz.TapByResourceRequest + 23, // 89: linkerd2.viz.Api.StatSummary:output_type -> linkerd2.viz.StatSummaryResponse + 29, // 90: linkerd2.viz.Api.Edges:output_type -> linkerd2.viz.EdgesResponse + 36, // 91: linkerd2.viz.Api.Gateways:output_type -> linkerd2.viz.GatewaysResponse + 32, // 92: linkerd2.viz.Api.TopRoutes:output_type -> linkerd2.viz.TopRoutesResponse + 8, // 93: linkerd2.viz.Api.ListPods:output_type -> linkerd2.viz.ListPodsResponse + 5, // 94: linkerd2.viz.Api.ListServices:output_type -> linkerd2.viz.ListServicesResponse + 67, // 95: linkerd2.viz.Api.SelfCheck:output_type -> linkerd2.common.healthcheck.SelfCheckResponse + 16, // 96: linkerd2.viz.Tap.Tap:output_type -> linkerd2.viz.TapEvent + 16, // 97: linkerd2.viz.Tap.TapByResource:output_type -> linkerd2.viz.TapEvent + 89, // [89:98] is the sub-list for method output_type + 80, // [80:89] is the sub-list for method input_type + 80, // [80:80] is the sub-list for extension type_name + 80, // [80:80] is the sub-list for extension extendee + 0, // [0:80] is the sub-list for field type_name } func init() { file_viz_proto_init() } @@ -5653,42 +5437,6 @@ func file_viz_proto_init() { } } file_viz_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IPAddress); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_viz_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IPv6); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_viz_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TcpAddress); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_viz_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Eos); i { case 0: return &v.state @@ -5700,7 +5448,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent); i { case 0: return &v.state @@ -5712,7 +5460,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApiError); i { case 0: return &v.state @@ -5724,7 +5472,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PodErrors); i { case 0: return &v.state @@ -5736,7 +5484,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Resource); i { case 0: return &v.state @@ -5748,7 +5496,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResourceSelection); i { case 0: return &v.state @@ -5760,7 +5508,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResourceError); i { case 0: return &v.state @@ -5772,7 +5520,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatSummaryRequest); i { case 0: return &v.state @@ -5784,7 +5532,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatSummaryResponse); i { case 0: return &v.state @@ -5796,7 +5544,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BasicStats); i { case 0: return &v.state @@ -5808,7 +5556,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TcpStats); i { case 0: return &v.state @@ -5820,7 +5568,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrafficSplitStats); i { case 0: return &v.state @@ -5832,7 +5580,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatTable); i { case 0: return &v.state @@ -5844,7 +5592,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EdgesRequest); i { case 0: return &v.state @@ -5856,7 +5604,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EdgesResponse); i { case 0: return &v.state @@ -5868,7 +5616,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Edge); i { case 0: return &v.state @@ -5880,7 +5628,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TopRoutesRequest); i { case 0: return &v.state @@ -5892,7 +5640,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TopRoutesResponse); i { case 0: return &v.state @@ -5904,7 +5652,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RouteTable); i { case 0: return &v.state @@ -5916,7 +5664,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GatewaysTable); i { case 0: return &v.state @@ -5928,7 +5676,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GatewaysRequest); i { case 0: return &v.state @@ -5940,7 +5688,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GatewaysResponse); i { case 0: return &v.state @@ -5952,7 +5700,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapByResourceRequest_Match); i { case 0: return &v.state @@ -5964,7 +5712,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapByResourceRequest_Extract); i { case 0: return &v.state @@ -5976,7 +5724,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapByResourceRequest_Match_Seq); i { case 0: return &v.state @@ -5988,7 +5736,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapByResourceRequest_Match_Http); i { case 0: return &v.state @@ -6000,7 +5748,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapByResourceRequest_Extract_Http); i { case 0: return &v.state @@ -6012,7 +5760,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapByResourceRequest_Extract_Http_Headers); i { case 0: return &v.state @@ -6024,7 +5772,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Headers_Header); i { case 0: return &v.state @@ -6036,7 +5784,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_EndpointMeta); i { case 0: return &v.state @@ -6048,7 +5796,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_RouteMeta); i { case 0: return &v.state @@ -6060,7 +5808,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_Http); i { case 0: return &v.state @@ -6072,7 +5820,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_Http_StreamId); i { case 0: return &v.state @@ -6084,7 +5832,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_Http_RequestInit); i { case 0: return &v.state @@ -6096,7 +5844,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_Http_ResponseInit); i { case 0: return &v.state @@ -6108,7 +5856,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TapEvent_Http_ResponseEnd); i { case 0: return &v.state @@ -6120,7 +5868,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PodErrors_PodError); i { case 0: return &v.state @@ -6132,7 +5880,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PodErrors_PodError_ContainerError); i { case 0: return &v.state @@ -6144,7 +5892,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatSummaryResponse_Ok); i { case 0: return &v.state @@ -6156,7 +5904,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatTable_PodGroup); i { case 0: return &v.state @@ -6168,7 +5916,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatTable_PodGroup_Row); i { case 0: return &v.state @@ -6180,7 +5928,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EdgesResponse_Ok); i { case 0: return &v.state @@ -6192,7 +5940,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TopRoutesResponse_Ok); i { case 0: return &v.state @@ -6204,7 +5952,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RouteTable_Row); i { case 0: return &v.state @@ -6216,7 +5964,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GatewaysTable_Row); i { case 0: return &v.state @@ -6228,7 +5976,7 @@ func file_viz_proto_init() { return nil } } - file_viz_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_viz_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GatewaysResponse_Ok); i { case 0: return &v.state @@ -6262,73 +6010,69 @@ func file_viz_proto_init() { (*Scheme_Unregistered)(nil), } file_viz_proto_msgTypes[12].OneofWrappers = []interface{}{ - (*IPAddress_Ipv4)(nil), - (*IPAddress_Ipv6)(nil), - } - file_viz_proto_msgTypes[15].OneofWrappers = []interface{}{ (*Eos_GrpcStatusCode)(nil), (*Eos_ResetErrorCode)(nil), } - file_viz_proto_msgTypes[16].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[13].OneofWrappers = []interface{}{ (*TapEvent_Http_)(nil), } - file_viz_proto_msgTypes[22].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[19].OneofWrappers = []interface{}{ (*StatSummaryRequest_None)(nil), (*StatSummaryRequest_ToResource)(nil), (*StatSummaryRequest_FromResource)(nil), } - file_viz_proto_msgTypes[23].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[20].OneofWrappers = []interface{}{ (*StatSummaryResponse_Ok_)(nil), (*StatSummaryResponse_Error)(nil), } - file_viz_proto_msgTypes[27].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[24].OneofWrappers = []interface{}{ (*StatTable_PodGroup_)(nil), } - file_viz_proto_msgTypes[29].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[26].OneofWrappers = []interface{}{ (*EdgesResponse_Ok_)(nil), (*EdgesResponse_Error)(nil), } - file_viz_proto_msgTypes[31].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[28].OneofWrappers = []interface{}{ (*TopRoutesRequest_None)(nil), (*TopRoutesRequest_ToResource)(nil), } - file_viz_proto_msgTypes[32].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[29].OneofWrappers = []interface{}{ (*TopRoutesResponse_Error)(nil), (*TopRoutesResponse_Ok_)(nil), } - file_viz_proto_msgTypes[36].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[33].OneofWrappers = []interface{}{ (*GatewaysResponse_Ok_)(nil), (*GatewaysResponse_Error)(nil), } - file_viz_proto_msgTypes[37].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[34].OneofWrappers = []interface{}{ (*TapByResourceRequest_Match_All)(nil), (*TapByResourceRequest_Match_Any)(nil), (*TapByResourceRequest_Match_Not)(nil), (*TapByResourceRequest_Match_Destinations)(nil), (*TapByResourceRequest_Match_Http_)(nil), } - file_viz_proto_msgTypes[38].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[35].OneofWrappers = []interface{}{ (*TapByResourceRequest_Extract_Http_)(nil), } - file_viz_proto_msgTypes[40].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[37].OneofWrappers = []interface{}{ (*TapByResourceRequest_Match_Http_Scheme)(nil), (*TapByResourceRequest_Match_Http_Method)(nil), (*TapByResourceRequest_Match_Http_Authority)(nil), (*TapByResourceRequest_Match_Http_Path)(nil), } - file_viz_proto_msgTypes[41].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[38].OneofWrappers = []interface{}{ (*TapByResourceRequest_Extract_Http_Headers_)(nil), } - file_viz_proto_msgTypes[43].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[40].OneofWrappers = []interface{}{ (*Headers_Header_ValueStr)(nil), (*Headers_Header_ValueBin)(nil), } - file_viz_proto_msgTypes[46].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[43].OneofWrappers = []interface{}{ (*TapEvent_Http_RequestInit_)(nil), (*TapEvent_Http_ResponseInit_)(nil), (*TapEvent_Http_ResponseEnd_)(nil), } - file_viz_proto_msgTypes[53].OneofWrappers = []interface{}{ + file_viz_proto_msgTypes[50].OneofWrappers = []interface{}{ (*PodErrors_PodError_Container)(nil), } type x struct{} @@ -6337,7 +6081,7 @@ func file_viz_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_viz_proto_rawDesc, NumEnums: 3, - NumMessages: 64, + NumMessages: 61, NumExtensions: 0, NumServices: 2, }, @@ -6370,12 +6114,6 @@ type ApiClient interface { TopRoutes(ctx context.Context, in *TopRoutesRequest, opts ...grpc.CallOption) (*TopRoutesResponse, error) ListPods(ctx context.Context, in *ListPodsRequest, opts ...grpc.CallOption) (*ListPodsResponse, error) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) - // Deprecated: Do not use. - // Superseded by `TapByResource`. - Tap(ctx context.Context, in *TapRequest, opts ...grpc.CallOption) (Api_TapClient, error) - // Deprecated: Do not use. - // Superseded by tap APIServer. - TapByResource(ctx context.Context, in *TapByResourceRequest, opts ...grpc.CallOption) (Api_TapByResourceClient, error) SelfCheck(ctx context.Context, in *healthcheck.SelfCheckRequest, opts ...grpc.CallOption) (*healthcheck.SelfCheckResponse, error) } @@ -6441,72 +6179,6 @@ func (c *apiClient) ListServices(ctx context.Context, in *ListServicesRequest, o return out, nil } -// Deprecated: Do not use. -func (c *apiClient) Tap(ctx context.Context, in *TapRequest, opts ...grpc.CallOption) (Api_TapClient, error) { - stream, err := c.cc.NewStream(ctx, &_Api_serviceDesc.Streams[0], "/linkerd2.viz.Api/Tap", opts...) - if err != nil { - return nil, err - } - x := &apiTapClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Api_TapClient interface { - Recv() (*TapEvent, error) - grpc.ClientStream -} - -type apiTapClient struct { - grpc.ClientStream -} - -func (x *apiTapClient) Recv() (*TapEvent, error) { - m := new(TapEvent) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// Deprecated: Do not use. -func (c *apiClient) TapByResource(ctx context.Context, in *TapByResourceRequest, opts ...grpc.CallOption) (Api_TapByResourceClient, error) { - stream, err := c.cc.NewStream(ctx, &_Api_serviceDesc.Streams[1], "/linkerd2.viz.Api/TapByResource", opts...) - if err != nil { - return nil, err - } - x := &apiTapByResourceClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Api_TapByResourceClient interface { - Recv() (*TapEvent, error) - grpc.ClientStream -} - -type apiTapByResourceClient struct { - grpc.ClientStream -} - -func (x *apiTapByResourceClient) Recv() (*TapEvent, error) { - m := new(TapEvent) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - func (c *apiClient) SelfCheck(ctx context.Context, in *healthcheck.SelfCheckRequest, opts ...grpc.CallOption) (*healthcheck.SelfCheckResponse, error) { out := new(healthcheck.SelfCheckResponse) err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/SelfCheck", in, out, opts...) @@ -6524,12 +6196,6 @@ type ApiServer interface { TopRoutes(context.Context, *TopRoutesRequest) (*TopRoutesResponse, error) ListPods(context.Context, *ListPodsRequest) (*ListPodsResponse, error) ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) - // Deprecated: Do not use. - // Superseded by `TapByResource`. - Tap(*TapRequest, Api_TapServer) error - // Deprecated: Do not use. - // Superseded by tap APIServer. - TapByResource(*TapByResourceRequest, Api_TapByResourceServer) error SelfCheck(context.Context, *healthcheck.SelfCheckRequest) (*healthcheck.SelfCheckResponse, error) } @@ -6555,12 +6221,6 @@ func (*UnimplementedApiServer) ListPods(context.Context, *ListPodsRequest) (*Lis func (*UnimplementedApiServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented") } -func (*UnimplementedApiServer) Tap(*TapRequest, Api_TapServer) error { - return status.Errorf(codes.Unimplemented, "method Tap not implemented") -} -func (*UnimplementedApiServer) TapByResource(*TapByResourceRequest, Api_TapByResourceServer) error { - return status.Errorf(codes.Unimplemented, "method TapByResource not implemented") -} func (*UnimplementedApiServer) SelfCheck(context.Context, *healthcheck.SelfCheckRequest) (*healthcheck.SelfCheckResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SelfCheck not implemented") } @@ -6677,48 +6337,6 @@ func _Api_ListServices_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } -func _Api_Tap_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(TapRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(ApiServer).Tap(m, &apiTapServer{stream}) -} - -type Api_TapServer interface { - Send(*TapEvent) error - grpc.ServerStream -} - -type apiTapServer struct { - grpc.ServerStream -} - -func (x *apiTapServer) Send(m *TapEvent) error { - return x.ServerStream.SendMsg(m) -} - -func _Api_TapByResource_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(TapByResourceRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(ApiServer).TapByResource(m, &apiTapByResourceServer{stream}) -} - -type Api_TapByResourceServer interface { - Send(*TapEvent) error - grpc.ServerStream -} - -type apiTapByResourceServer struct { - grpc.ServerStream -} - -func (x *apiTapByResourceServer) Send(m *TapEvent) error { - return x.ServerStream.SendMsg(m) -} - func _Api_SelfCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(healthcheck.SelfCheckRequest) if err := dec(in); err != nil { @@ -6770,18 +6388,7 @@ var _Api_serviceDesc = grpc.ServiceDesc{ Handler: _Api_SelfCheck_Handler, }, }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Tap", - Handler: _Api_Tap_Handler, - ServerStreams: true, - }, - { - StreamName: "TapByResource", - Handler: _Api_TapByResource_Handler, - ServerStreams: true, - }, - }, + Streams: []grpc.StreamDesc{}, Metadata: "viz.proto", } diff --git a/viz/metrics-api/grpc_server.go b/viz/metrics-api/grpc_server.go new file mode 100644 index 0000000000000..82d597e3f511d --- /dev/null +++ b/viz/metrics-api/grpc_server.go @@ -0,0 +1,240 @@ +package api + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/golang/protobuf/ptypes/duration" + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + "github.com/linkerd/linkerd2/controller/k8s" + pkgK8s "github.com/linkerd/linkerd2/pkg/k8s" + "github.com/linkerd/linkerd2/pkg/prometheus" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + "github.com/linkerd/linkerd2/viz/metrics-api/util" + promv1 "github.com/prometheus/client_golang/api/prometheus/v1" + log "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" +) + +// Server specifies the interface the Viz metric API server should implement +type Server interface { + pb.ApiServer +} + +type grpcServer struct { + prometheusAPI promv1.API + k8sAPI *k8s.API + controllerNamespace string + clusterDomain string + ignoredNamespaces []string +} + +type podReport struct { + lastReport time.Time + processStartTimeSeconds time.Time +} + +const ( + podQuery = "max(process_start_time_seconds{%s}) by (pod, namespace)" + k8sClientSubsystemName = "kubernetes" + k8sClientCheckDescription = "linkerd viz can talk to Kubernetes" + promClientSubsystemName = "prometheus" + promClientCheckDescription = "linkerd viz can talk to Prometheus" +) + +func newGrpcServer( + promAPI promv1.API, + k8sAPI *k8s.API, + controllerNamespace string, + clusterDomain string, + ignoredNamespaces []string, +) *grpcServer { + + grpcServer := &grpcServer{ + prometheusAPI: promAPI, + k8sAPI: k8sAPI, + controllerNamespace: controllerNamespace, + clusterDomain: clusterDomain, + ignoredNamespaces: ignoredNamespaces, + } + + pb.RegisterApiServer(prometheus.NewGrpcServer(), grpcServer) + + return grpcServer +} + +func (s *grpcServer) ListPods(ctx context.Context, req *pb.ListPodsRequest) (*pb.ListPodsResponse, error) { + log.Debugf("ListPods request: %+v", req) + + targetOwner := req.GetSelector().GetResource() + + // Reports is a map from instance name to the absolute time of the most recent + // report from that instance and its process start time + reports := make(map[string]podReport) + + if req.GetNamespace() != "" && req.GetSelector() != nil { + return nil, errors.New("cannot set both namespace and resource in the request. These are mutually exclusive") + } + + labelSelector := labels.Everything() + if s := req.GetSelector().GetLabelSelector(); s != "" { + var err error + labelSelector, err = labels.Parse(s) + if err != nil { + return nil, fmt.Errorf("invalid label selector \"%s\": %s", s, err) + } + } + + nsQuery := "" + namespace := "" + if req.GetNamespace() != "" { + namespace = req.GetNamespace() + } else if targetOwner.GetNamespace() != "" { + namespace = targetOwner.GetNamespace() + } else if targetOwner.GetType() == pkgK8s.Namespace { + namespace = targetOwner.GetName() + } + if namespace != "" { + nsQuery = fmt.Sprintf("namespace=\"%s\"", namespace) + } + processStartTimeQuery := fmt.Sprintf(podQuery, nsQuery) + + // Query Prometheus for all pods present + vec, err := s.queryProm(ctx, processStartTimeQuery) + if err != nil && !errors.Is(err, ErrNoPrometheusInstance) { + return nil, err + } + + for _, sample := range vec { + pod := string(sample.Metric["pod"]) + timestamp := sample.Timestamp + + reports[pod] = podReport{ + lastReport: time.Unix(0, int64(timestamp)*int64(time.Millisecond)), + processStartTimeSeconds: time.Unix(0, int64(sample.Value)*int64(time.Second)), + } + } + + var pods []*corev1.Pod + if namespace != "" { + pods, err = s.k8sAPI.Pod().Lister().Pods(namespace).List(labelSelector) + } else { + pods, err = s.k8sAPI.Pod().Lister().List(labelSelector) + } + + if err != nil { + return nil, err + } + podList := make([]*pb.Pod, 0) + + for _, pod := range pods { + if s.shouldIgnore(pod) { + continue + } + + ownerKind, ownerName := s.k8sAPI.GetOwnerKindAndName(ctx, pod, false) + // filter out pods without matching owner + if targetOwner.GetNamespace() != "" && targetOwner.GetNamespace() != pod.GetNamespace() { + continue + } + if targetOwner.GetType() != "" && targetOwner.GetType() != ownerKind { + continue + } + if targetOwner.GetName() != "" && targetOwner.GetName() != ownerName { + continue + } + + updated, added := reports[pod.Name] + + item := util.K8sPodToPublicPod(*pod, ownerKind, ownerName) + item.Added = added + + if added { + since := time.Since(updated.lastReport) + item.SinceLastReport = &duration.Duration{ + Seconds: int64(since / time.Second), + Nanos: int32(since % time.Second), + } + sinceStarting := time.Since(updated.processStartTimeSeconds) + item.Uptime = &duration.Duration{ + Seconds: int64(sinceStarting / time.Second), + Nanos: int32(sinceStarting % time.Second), + } + } + + podList = append(podList, item) + } + + rsp := pb.ListPodsResponse{Pods: podList} + + log.Debugf("ListPods response: %s", rsp.String()) + + return &rsp, nil +} + +func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) { + k8sClientCheck := &healthcheckPb.CheckResult{ + SubsystemName: k8sClientSubsystemName, + CheckDescription: k8sClientCheckDescription, + Status: healthcheckPb.CheckStatus_OK, + } + _, err := s.k8sAPI.Pod().Lister().List(labels.Everything()) + if err != nil { + k8sClientCheck.Status = healthcheckPb.CheckStatus_ERROR + k8sClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling the Kubernetes API: %s", err) + } + + response := &healthcheckPb.SelfCheckResponse{ + Results: []*healthcheckPb.CheckResult{ + k8sClientCheck, + }, + } + + if s.prometheusAPI != nil { + promClientCheck := &healthcheckPb.CheckResult{ + SubsystemName: promClientSubsystemName, + CheckDescription: promClientCheckDescription, + Status: healthcheckPb.CheckStatus_OK, + } + _, err = s.queryProm(ctx, fmt.Sprintf(podQuery, "")) + if err != nil { + promClientCheck.Status = healthcheckPb.CheckStatus_ERROR + promClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling Prometheus from the control plane: %s", err) + } + + response.Results = append(response.Results, promClientCheck) + } + + return response, nil +} + +func (s *grpcServer) shouldIgnore(pod *corev1.Pod) bool { + for _, namespace := range s.ignoredNamespaces { + if pod.Namespace == namespace { + return true + } + } + return false +} + +func (s *grpcServer) ListServices(ctx context.Context, req *pb.ListServicesRequest) (*pb.ListServicesResponse, error) { + log.Debugf("ListServices request: %+v", req) + + services, err := s.k8sAPI.GetServices(req.Namespace, "") + if err != nil { + return nil, err + } + + svcs := make([]*pb.Service, 0) + for _, svc := range services { + svcs = append(svcs, &pb.Service{ + Name: svc.GetName(), + Namespace: svc.GetNamespace(), + }) + } + + return &pb.ListServicesResponse{Services: svcs}, nil +} diff --git a/controller/api/public/grpc_server_test.go b/viz/metrics-api/grpc_server_test.go similarity index 99% rename from controller/api/public/grpc_server_test.go rename to viz/metrics-api/grpc_server_test.go index ab9b42fcaf538..70ec8e5799876 100644 --- a/controller/api/public/grpc_server_test.go +++ b/viz/metrics-api/grpc_server_test.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" @@ -398,7 +398,6 @@ status: fakeGrpcServer := newGrpcServer( &mProm, - nil, k8sAPI, "linkerd", "mycluster.local", @@ -500,7 +499,6 @@ metadata: fakeGrpcServer := newGrpcServer( &MockProm{}, - nil, k8sAPI, "linkerd", "mycluster.local", diff --git a/viz/metrics-api/http_server.go b/viz/metrics-api/http_server.go new file mode 100644 index 0000000000000..93bf4394d2a5e --- /dev/null +++ b/viz/metrics-api/http_server.go @@ -0,0 +1,248 @@ +package api + +import ( + "fmt" + "net/http" + + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + "github.com/linkerd/linkerd2/controller/k8s" + "github.com/linkerd/linkerd2/pkg/prometheus" + "github.com/linkerd/linkerd2/pkg/protohttp" + "github.com/linkerd/linkerd2/viz/metrics-api/client" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + promApi "github.com/prometheus/client_golang/api" + promv1 "github.com/prometheus/client_golang/api/prometheus/v1" + log "github.com/sirupsen/logrus" +) + +var ( + gatewaysPath = fullURLPathFor("Gateways") + statSummaryPath = fullURLPathFor("StatSummary") + topRoutesPath = fullURLPathFor("TopRoutes") + listPodsPath = fullURLPathFor("ListPods") + listServicesPath = fullURLPathFor("ListServices") + selfCheckPath = fullURLPathFor("SelfCheck") + edgesPath = fullURLPathFor("Edges") +) + +type handler struct { + grpcServer Server +} + +func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + log.WithFields(log.Fields{ + "req.Method": req.Method, "req.URL": req.URL, "req.Form": req.Form, + }).Debugf("Serving %s %s", req.Method, req.URL.Path) + // Validate request method + if req.Method != http.MethodPost { + protohttp.WriteErrorToHTTPResponse(w, fmt.Errorf("POST required")) + return + } + + // Serve request + switch req.URL.Path { + case gatewaysPath: + h.handleGateways(w, req) + case statSummaryPath: + h.handleStatSummary(w, req) + case topRoutesPath: + h.handleTopRoutes(w, req) + case listPodsPath: + h.handleListPods(w, req) + case listServicesPath: + h.handleListServices(w, req) + case selfCheckPath: + h.handleSelfCheck(w, req) + case edgesPath: + h.handleEdges(w, req) + default: + http.NotFound(w, req) + } + +} + +func (h *handler) handleGateways(w http.ResponseWriter, req *http.Request) { + var protoRequest pb.GatewaysRequest + + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.Gateways(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func (h *handler) handleStatSummary(w http.ResponseWriter, req *http.Request) { + var protoRequest pb.StatSummaryRequest + + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.StatSummary(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func (h *handler) handleEdges(w http.ResponseWriter, req *http.Request) { + var protoRequest pb.EdgesRequest + + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.Edges(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func (h *handler) handleTopRoutes(w http.ResponseWriter, req *http.Request) { + var protoRequest pb.TopRoutesRequest + + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.TopRoutes(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func (h *handler) handleSelfCheck(w http.ResponseWriter, req *http.Request) { + var protoRequest healthcheckPb.SelfCheckRequest + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.SelfCheck(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func (h *handler) handleListPods(w http.ResponseWriter, req *http.Request) { + var protoRequest pb.ListPodsRequest + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.ListPods(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func (h *handler) handleListServices(w http.ResponseWriter, req *http.Request) { + var protoRequest pb.ListServicesRequest + + err := protohttp.HTTPRequestToProto(req, &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + rsp, err := h.grpcServer.ListServices(req.Context(), &protoRequest) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } + + err = protohttp.WriteProtoToHTTPResponse(w, rsp) + if err != nil { + protohttp.WriteErrorToHTTPResponse(w, err) + return + } +} + +func fullURLPathFor(method string) string { + return client.APIRoot + client.APIPrefix + method +} + +// NewServer creates a Public API HTTP server. +func NewServer( + addr string, + prometheusClient promApi.Client, + k8sAPI *k8s.API, + controllerNamespace string, + clusterDomain string, + ignoredNamespaces []string, +) *http.Server { + + var promAPI promv1.API + if prometheusClient != nil { + promAPI = promv1.NewAPI(prometheusClient) + } + + grpcServer := newGrpcServer( + promAPI, + k8sAPI, + controllerNamespace, + clusterDomain, + ignoredNamespaces, + ) + baseHandler := &handler{ + grpcServer: grpcServer, + } + + instrumentedHandler := prometheus.WithTelemetry(baseHandler) + + return &http.Server{ + Addr: addr, + Handler: instrumentedHandler, + } +} diff --git a/viz/metrics-api/http_server_test.go b/viz/metrics-api/http_server_test.go new file mode 100644 index 0000000000000..b7460f070996c --- /dev/null +++ b/viz/metrics-api/http_server_test.go @@ -0,0 +1,141 @@ +package api + +import ( + "context" + "errors" + "net" + "net/http" + "testing" + + "github.com/golang/protobuf/proto" + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + vizClient "github.com/linkerd/linkerd2/viz/metrics-api/client" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" +) + +type mockServer struct { + LastRequestReceived proto.Message + ResponseToReturn proto.Message + ErrorToReturn error +} + +type mockGrpcServer struct { + mockServer +} + +func (m *mockGrpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest) (*pb.StatSummaryResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*pb.StatSummaryResponse), m.ErrorToReturn +} + +func (m *mockGrpcServer) Gateways(ctx context.Context, req *pb.GatewaysRequest) (*pb.GatewaysResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*pb.GatewaysResponse), m.ErrorToReturn +} + +func (m *mockGrpcServer) TopRoutes(ctx context.Context, req *pb.TopRoutesRequest) (*pb.TopRoutesResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*pb.TopRoutesResponse), m.ErrorToReturn +} + +func (m *mockGrpcServer) Edges(ctx context.Context, req *pb.EdgesRequest) (*pb.EdgesResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*pb.EdgesResponse), m.ErrorToReturn +} + +func (m *mockGrpcServer) ListPods(ctx context.Context, req *pb.ListPodsRequest) (*pb.ListPodsResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*pb.ListPodsResponse), m.ErrorToReturn +} + +func (m *mockGrpcServer) ListServices(ctx context.Context, req *pb.ListServicesRequest) (*pb.ListServicesResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*pb.ListServicesResponse), m.ErrorToReturn +} + +func (m *mockGrpcServer) SelfCheck(ctx context.Context, req *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) { + m.LastRequestReceived = req + return m.ResponseToReturn.(*healthcheckPb.SelfCheckResponse), m.ErrorToReturn +} + +type grpcCallTestCase struct { + expectedRequest proto.Message + expectedResponse proto.Message + functionCall func() (proto.Message, error) +} + +func TestServer(t *testing.T) { + t.Run("Delegates all non-streaming RPC messages to the underlying grpc server", func(t *testing.T) { + mockGrpcServer, client := getServerVizClient(t) + + listPodsReq := &pb.ListPodsRequest{} + testListPods := grpcCallTestCase{ + expectedRequest: listPodsReq, + expectedResponse: &pb.ListPodsResponse{ + Pods: []*pb.Pod{ + {Status: "ok-ish"}, + }, + }, + functionCall: func() (proto.Message, error) { return client.ListPods(context.TODO(), listPodsReq) }, + } + + statSummaryReq := &pb.StatSummaryRequest{} + testStatSummary := grpcCallTestCase{ + expectedRequest: statSummaryReq, + expectedResponse: &pb.StatSummaryResponse{}, + functionCall: func() (proto.Message, error) { return client.StatSummary(context.TODO(), statSummaryReq) }, + } + + for _, testCase := range []grpcCallTestCase{testListPods, testStatSummary} { + assertCallWasForwarded(t, &mockGrpcServer.mockServer, testCase.expectedRequest, testCase.expectedResponse, testCase.functionCall) + } + }) +} + +func getServerVizClient(t *testing.T) (*mockGrpcServer, pb.ApiClient) { + mockGrpcServer := &mockGrpcServer{} + + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Could not start listener: %v", err) + } + + go func() { + handler := &handler{ + grpcServer: mockGrpcServer, + } + err := http.Serve(listener, handler) + if err != nil { + t.Fatalf("Could not start server: %v", err) + } + }() + + client, err := vizClient.NewInternalClient("linkerd", listener.Addr().String()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + return mockGrpcServer, client +} + +func assertCallWasForwarded(t *testing.T, mockServer *mockServer, expectedRequest proto.Message, expectedResponse proto.Message, functionCall func() (proto.Message, error)) { + mockServer.ErrorToReturn = nil + mockServer.ResponseToReturn = expectedResponse + actualResponse, err := functionCall() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + actualRequest := mockServer.LastRequestReceived + if !proto.Equal(actualRequest, expectedRequest) { + t.Fatalf("Expecting server call to return [%v], but got [%v]", expectedRequest, actualRequest) + } + if !proto.Equal(actualResponse, expectedResponse) { + t.Fatalf("Expecting server call to return [%v], but got [%v]", expectedResponse, actualResponse) + } + + mockServer.ErrorToReturn = errors.New("expected") + _, err = functionCall() + if err == nil { + t.Fatalf("Expecting error, got nothing") + } +} diff --git a/controller/api/public/prometheus.go b/viz/metrics-api/prometheus.go similarity index 99% rename from controller/api/public/prometheus.go rename to viz/metrics-api/prometheus.go index 7c3b472e85bf9..4737a8d2f2ee3 100644 --- a/controller/api/public/prometheus.go +++ b/viz/metrics-api/prometheus.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/viz/metrics-api/proto/viz.proto b/viz/metrics-api/proto/viz.proto index b3f63814e85ae..b44d1e04ad0a1 100644 --- a/viz/metrics-api/proto/viz.proto +++ b/viz/metrics-api/proto/viz.proto @@ -3,8 +3,8 @@ syntax = "proto3"; package linkerd2.viz; import "google/protobuf/duration.proto"; - -import "healthcheck.proto"; +import "common/healthcheck.proto"; +import "common/net.proto"; option go_package = "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"; @@ -181,23 +181,6 @@ message Headers { repeated Header headers = 1; } -message IPAddress { - oneof ip { - fixed32 ipv4 = 1; - IPv6 ipv6 = 2; - } -} - -message IPv6 { - fixed64 first = 1; // hextets 1-4 - fixed64 last = 2; // hextets 5-8 -} - -message TcpAddress { - IPAddress ip = 1; - uint32 port = 2; -} - message Eos { oneof end { uint32 grpc_status_code = 1; @@ -207,10 +190,10 @@ message Eos { // This is used only by the tap APIServer. message TapEvent { - TcpAddress source = 1; + linkerd2.common.net.TcpAddress source = 1; EndpointMeta source_meta = 5; - TcpAddress destination = 2; + linkerd2.common.net.TcpAddress destination = 2; EndpointMeta destination_meta = 4; RouteMeta route_meta = 7; @@ -521,13 +504,7 @@ service Api { rpc ListServices(ListServicesRequest) returns (ListServicesResponse) {} - // Superseded by `TapByResource`. - rpc Tap(TapRequest) returns (stream TapEvent) { option deprecated = true; } - - // Superseded by tap APIServer. - rpc TapByResource(TapByResourceRequest) returns (stream TapEvent) { option deprecated = true; } - - rpc SelfCheck(healthcheck.SelfCheckRequest) returns (healthcheck.SelfCheckResponse) {} + rpc SelfCheck(linkerd2.common.healthcheck.SelfCheckRequest) returns (linkerd2.common.healthcheck.SelfCheckResponse) {} } diff --git a/controller/api/public/stat_summary.go b/viz/metrics-api/stat_summary.go similarity index 99% rename from controller/api/public/stat_summary.go rename to viz/metrics-api/stat_summary.go index 27759318134b4..2c26bb2ea005e 100644 --- a/controller/api/public/stat_summary.go +++ b/viz/metrics-api/stat_summary.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/controller/api/public/stat_summary_test.go b/viz/metrics-api/stat_summary_test.go similarity index 99% rename from controller/api/public/stat_summary_test.go rename to viz/metrics-api/stat_summary_test.go index 9740773aaf23b..ca1b9818e2785 100644 --- a/controller/api/public/stat_summary_test.go +++ b/viz/metrics-api/stat_summary_test.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" @@ -1414,7 +1414,6 @@ status: for _, exp := range expectations { fakeGrpcServer := newGrpcServer( &MockProm{Res: exp.mockPromResponse}, - nil, k8sAPI, "linkerd", "mycluster.local", @@ -1439,7 +1438,6 @@ status: } fakeGrpcServer := newGrpcServer( &MockProm{Res: model.Vector{}}, - nil, k8sAPI, "linkerd", "mycluster.local", diff --git a/viz/metrics-api/test_helper.go b/viz/metrics-api/test_helper.go new file mode 100644 index 0000000000000..5eaee8dad460a --- /dev/null +++ b/viz/metrics-api/test_helper.go @@ -0,0 +1,512 @@ +package api + +import ( + "context" + "fmt" + "reflect" + "sort" + "sync" + "time" + + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + "github.com/linkerd/linkerd2/controller/k8s" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + promv1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" + "google.golang.org/grpc" +) + +// MockAPIClient satisfies the Public API's gRPC interfaces (public.APIClient). +type MockAPIClient struct { + ErrorToReturn error + ListPodsResponseToReturn *pb.ListPodsResponse + ListServicesResponseToReturn *pb.ListServicesResponse + StatSummaryResponseToReturn *pb.StatSummaryResponse + GatewaysResponseToReturn *pb.GatewaysResponse + TopRoutesResponseToReturn *pb.TopRoutesResponse + EdgesResponseToReturn *pb.EdgesResponse + SelfCheckResponseToReturn *healthcheckPb.SelfCheckResponse +} + +// StatSummary provides a mock of a Public API method. +func (c *MockAPIClient) StatSummary(ctx context.Context, in *pb.StatSummaryRequest, opts ...grpc.CallOption) (*pb.StatSummaryResponse, error) { + return c.StatSummaryResponseToReturn, c.ErrorToReturn +} + +// Gateways provides a mock of a Public API method. +func (c *MockAPIClient) Gateways(ctx context.Context, in *pb.GatewaysRequest, opts ...grpc.CallOption) (*pb.GatewaysResponse, error) { + return c.GatewaysResponseToReturn, c.ErrorToReturn +} + +// TopRoutes provides a mock of a Public API method. +func (c *MockAPIClient) TopRoutes(ctx context.Context, in *pb.TopRoutesRequest, opts ...grpc.CallOption) (*pb.TopRoutesResponse, error) { + return c.TopRoutesResponseToReturn, c.ErrorToReturn +} + +// Edges provides a mock of a Public API method. +func (c *MockAPIClient) Edges(ctx context.Context, in *pb.EdgesRequest, opts ...grpc.CallOption) (*pb.EdgesResponse, error) { + return c.EdgesResponseToReturn, c.ErrorToReturn +} + +// ListPods provides a mock of a Public API method. +func (c *MockAPIClient) ListPods(ctx context.Context, in *pb.ListPodsRequest, opts ...grpc.CallOption) (*pb.ListPodsResponse, error) { + return c.ListPodsResponseToReturn, c.ErrorToReturn +} + +// ListServices provides a mock of a Public API method. +func (c *MockAPIClient) ListServices(ctx context.Context, in *pb.ListServicesRequest, opts ...grpc.CallOption) (*pb.ListServicesResponse, error) { + return c.ListServicesResponseToReturn, c.ErrorToReturn +} + +// SelfCheck provides a mock of a Public API method. +func (c *MockAPIClient) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) { + return c.SelfCheckResponseToReturn, c.ErrorToReturn +} + +// +// Prometheus client +// + +// MockProm satisfies the promv1.API interface for testing. +// TODO: move this into something shared under /controller, or into /pkg +type MockProm struct { + Res model.Value + QueriesExecuted []string // expose the queries our Mock Prometheus receives, to test query generation + rwLock sync.Mutex +} + +// PodCounts is a test helper struct that is used for representing data in a +// StatTable.PodGroup.Row. +type PodCounts struct { + Status string + MeshedPods uint64 + RunningPods uint64 + FailedPods uint64 + Errors map[string]*pb.PodErrors +} + +// Query performs a query for the given time. +func (m *MockProm) Query(ctx context.Context, query string, ts time.Time) (model.Value, promv1.Warnings, error) { + m.rwLock.Lock() + defer m.rwLock.Unlock() + m.QueriesExecuted = append(m.QueriesExecuted, query) + return m.Res, nil, nil +} + +// QueryRange performs a query for the given range. +func (m *MockProm) QueryRange(ctx context.Context, query string, r promv1.Range) (model.Value, promv1.Warnings, error) { + m.rwLock.Lock() + defer m.rwLock.Unlock() + m.QueriesExecuted = append(m.QueriesExecuted, query) + return m.Res, nil, nil +} + +// AlertManagers returns an overview of the current state of the Prometheus alert +// manager discovery. +func (m *MockProm) AlertManagers(ctx context.Context) (promv1.AlertManagersResult, error) { + return promv1.AlertManagersResult{}, nil +} + +// Alerts returns a list of all active alerts. +func (m *MockProm) Alerts(ctx context.Context) (promv1.AlertsResult, error) { + return promv1.AlertsResult{}, nil +} + +// CleanTombstones removes the deleted data from disk and cleans up the existing +// tombstones. +func (m *MockProm) CleanTombstones(ctx context.Context) error { + return nil +} + +// Config returns the current Prometheus configuration. +func (m *MockProm) Config(ctx context.Context) (promv1.ConfigResult, error) { + return promv1.ConfigResult{}, nil +} + +// DeleteSeries deletes data for a selection of series in a time range. +func (m *MockProm) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error { + return nil +} + +// Flags returns the flag values that Prometheus was launched with. +func (m *MockProm) Flags(ctx context.Context) (promv1.FlagsResult, error) { + return promv1.FlagsResult{}, nil +} + +// LabelValues performs a query for the values of the given label. +func (m *MockProm) LabelValues(ctx context.Context, label string, startTime time.Time, endTime time.Time) (model.LabelValues, promv1.Warnings, error) { + return nil, nil, nil +} + +// Series finds series by label matchers. +func (m *MockProm) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, promv1.Warnings, error) { + return nil, nil, nil +} + +// Snapshot creates a snapshot of all current data into +// snapshots/- under the TSDB's data directory and returns the +// directory as response. +func (m *MockProm) Snapshot(ctx context.Context, skipHead bool) (promv1.SnapshotResult, error) { + return promv1.SnapshotResult{}, nil +} + +// Targets returns an overview of the current state of the Prometheus target +// discovery. +func (m *MockProm) Targets(ctx context.Context) (promv1.TargetsResult, error) { + return promv1.TargetsResult{}, nil +} + +// LabelNames returns all the unique label names present in the block in sorted order. +func (m *MockProm) LabelNames(ctx context.Context, startTime time.Time, endTime time.Time) ([]string, promv1.Warnings, error) { + return []string{}, nil, nil +} + +// Runtimeinfo returns the runtime info about Prometheus +func (m *MockProm) Runtimeinfo(ctx context.Context) (promv1.RuntimeinfoResult, error) { + return promv1.RuntimeinfoResult{}, nil +} + +// Metadata returns the metadata of the specified metric +func (m *MockProm) Metadata(ctx context.Context, metric string, limit string) (map[string][]promv1.Metadata, error) { + return nil, nil +} + +// Rules returns a list of alerting and recording rules that are currently loaded. +func (m *MockProm) Rules(ctx context.Context) (promv1.RulesResult, error) { + return promv1.RulesResult{}, nil +} + +// TargetsMetadata returns metadata about metrics currently scraped by the target. +func (m *MockProm) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]promv1.MetricMetadata, error) { + return []promv1.MetricMetadata{}, nil +} + +// GenStatSummaryResponse generates a mock Public API StatSummaryResponse +// object. +func GenStatSummaryResponse(resName, resType string, resNs []string, counts *PodCounts, basicStats bool, tcpStats bool) *pb.StatSummaryResponse { + rows := []*pb.StatTable_PodGroup_Row{} + for _, ns := range resNs { + statTableRow := &pb.StatTable_PodGroup_Row{ + Resource: &pb.Resource{ + Namespace: ns, + Type: resType, + Name: resName, + }, + TimeWindow: "1m", + } + + if basicStats { + statTableRow.Stats = &pb.BasicStats{ + SuccessCount: 123, + FailureCount: 0, + LatencyMsP50: 123, + LatencyMsP95: 123, + LatencyMsP99: 123, + } + } + + if tcpStats { + statTableRow.TcpStats = &pb.TcpStats{ + OpenConnections: 123, + ReadBytesTotal: 123, + WriteBytesTotal: 123, + } + } + + if counts != nil { + statTableRow.MeshedPodCount = counts.MeshedPods + statTableRow.RunningPodCount = counts.RunningPods + statTableRow.FailedPodCount = counts.FailedPods + statTableRow.Status = counts.Status + statTableRow.ErrorsByPod = counts.Errors + } + + rows = append(rows, statTableRow) + } + + resp := &pb.StatSummaryResponse{ + Response: &pb.StatSummaryResponse_Ok_{ // /~https://github.com/golang/protobuf/issues/205 + Ok: &pb.StatSummaryResponse_Ok{ + StatTables: []*pb.StatTable{ + { + Table: &pb.StatTable_PodGroup_{ + PodGroup: &pb.StatTable_PodGroup{ + Rows: rows, + }, + }, + }, + }, + }, + }, + } + + return resp +} + +// GenStatTsResponse generates a mock Public API StatSummaryResponse +// object in response to a request for trafficsplit stats. +func GenStatTsResponse(resName, resType string, resNs []string, basicStats bool, tsStats bool) *pb.StatSummaryResponse { + leaves := map[string]string{ + "service-1": "900m", + "service-2": "100m", + } + apex := "apex_name" + + rows := []*pb.StatTable_PodGroup_Row{} + for _, ns := range resNs { + for name, weight := range leaves { + statTableRow := &pb.StatTable_PodGroup_Row{ + Resource: &pb.Resource{ + Namespace: ns, + Type: resType, + Name: resName, + }, + TimeWindow: "1m", + } + + if basicStats { + statTableRow.Stats = &pb.BasicStats{ + SuccessCount: 123, + FailureCount: 0, + LatencyMsP50: 123, + LatencyMsP95: 123, + LatencyMsP99: 123, + } + } + + if tsStats { + statTableRow.TsStats = &pb.TrafficSplitStats{ + Apex: apex, + Leaf: name, + Weight: weight, + } + } + rows = append(rows, statTableRow) + + } + } + + // sort rows before returning in order to have a consistent order for tests + rows = sortTrafficSplitRows(rows) + + resp := &pb.StatSummaryResponse{ + Response: &pb.StatSummaryResponse_Ok_{ // /~https://github.com/golang/protobuf/issues/205 + Ok: &pb.StatSummaryResponse_Ok{ + StatTables: []*pb.StatTable{ + { + Table: &pb.StatTable_PodGroup_{ + PodGroup: &pb.StatTable_PodGroup{ + Rows: rows, + }, + }, + }, + }, + }, + }, + } + return resp +} + +type mockEdgeRow struct { + resourceType string + src string + dst string + srcNamespace string + dstNamespace string + clientID string + serverID string + msg string +} + +// a slice of edge rows to generate mock results +var emojivotoEdgeRows = []*mockEdgeRow{ + { + resourceType: "deployment", + src: "web", + dst: "voting", + srcNamespace: "emojivoto", + dstNamespace: "emojivoto", + clientID: "web.emojivoto.serviceaccount.identity.linkerd.cluster.local", + serverID: "voting.emojivoto.serviceaccount.identity.linkerd.cluster.local", + msg: "", + }, + { + resourceType: "deployment", + src: "vote-bot", + dst: "web", + srcNamespace: "emojivoto", + dstNamespace: "emojivoto", + clientID: "default.emojivoto.serviceaccount.identity.linkerd.cluster.local", + serverID: "web.emojivoto.serviceaccount.identity.linkerd.cluster.local", + msg: "", + }, + { + resourceType: "deployment", + src: "web", + dst: "emoji", + srcNamespace: "emojivoto", + dstNamespace: "emojivoto", + clientID: "web.emojivoto.serviceaccount.identity.linkerd.cluster.local", + serverID: "emoji.emojivoto.serviceaccount.identity.linkerd.cluster.local", + msg: "", + }, +} + +// a slice of edge rows to generate mock results +var linkerdEdgeRows = []*mockEdgeRow{ + { + resourceType: "deployment", + src: "linkerd-controller", + dst: "linkerd-prometheus", + srcNamespace: "linkerd", + dstNamespace: "linkerd", + clientID: "linkerd-controller.linkerd.identity.linkerd.cluster.local", + serverID: "linkerd-prometheus.linkerd.identity.linkerd.cluster.local", + msg: "", + }, +} + +// GenEdgesResponse generates a mock Public API EdgesResponse +// object. +func GenEdgesResponse(resourceType string, edgeRowNamespace string) *pb.EdgesResponse { + edgeRows := emojivotoEdgeRows + + if edgeRowNamespace == "linkerd" { + edgeRows = linkerdEdgeRows + } else if edgeRowNamespace == "all" { + // combine emojivotoEdgeRows and linkerdEdgeRows + edgeRows = append(edgeRows, linkerdEdgeRows...) + } + + edges := []*pb.Edge{} + for _, row := range edgeRows { + edge := &pb.Edge{ + Src: &pb.Resource{ + Name: row.src, + Namespace: row.srcNamespace, + Type: row.resourceType, + }, + Dst: &pb.Resource{ + Name: row.dst, + Namespace: row.dstNamespace, + Type: row.resourceType, + }, + ClientId: row.clientID, + ServerId: row.serverID, + NoIdentityMsg: row.msg, + } + edges = append(edges, edge) + } + + // sorting to retain consistent order for tests + edges = sortEdgeRows(edges) + + resp := &pb.EdgesResponse{ + Response: &pb.EdgesResponse_Ok_{ + Ok: &pb.EdgesResponse_Ok{ + Edges: edges, + }, + }, + } + return resp +} + +// GenTopRoutesResponse generates a mock Public API TopRoutesResponse object. +func GenTopRoutesResponse(routes []string, counts []uint64, outbound bool, authority string) *pb.TopRoutesResponse { + rows := []*pb.RouteTable_Row{} + for i, route := range routes { + row := &pb.RouteTable_Row{ + Route: route, + Authority: authority, + Stats: &pb.BasicStats{ + SuccessCount: counts[i], + FailureCount: 0, + LatencyMsP50: 123, + LatencyMsP95: 123, + LatencyMsP99: 123, + }, + TimeWindow: "1m", + } + if outbound { + row.Stats.ActualSuccessCount = counts[i] + } + rows = append(rows, row) + } + defaultRow := &pb.RouteTable_Row{ + Route: "[DEFAULT]", + Authority: authority, + Stats: &pb.BasicStats{ + SuccessCount: counts[len(counts)-1], + FailureCount: 0, + LatencyMsP50: 123, + LatencyMsP95: 123, + LatencyMsP99: 123, + }, + TimeWindow: "1m", + } + if outbound { + defaultRow.Stats.ActualSuccessCount = counts[len(counts)-1] + } + rows = append(rows, defaultRow) + + resp := &pb.TopRoutesResponse{ + Response: &pb.TopRoutesResponse_Ok_{ + Ok: &pb.TopRoutesResponse_Ok{ + Routes: []*pb.RouteTable{ + { + Rows: rows, + Resource: "deploy/foobar", + }, + }, + }, + }, + } + + return resp +} + +type expectedStatRPC struct { + err error + k8sConfigs []string // k8s objects to seed the API + mockPromResponse model.Value // mock out a prometheus query response + expectedPrometheusQueries []string // queries we expect public-api to issue to prometheus +} + +func newMockGrpcServer(exp expectedStatRPC) (*MockProm, *grpcServer, error) { + k8sAPI, err := k8s.NewFakeAPI(exp.k8sConfigs...) + if err != nil { + return nil, nil, err + } + + mockProm := &MockProm{Res: exp.mockPromResponse} + fakeGrpcServer := newGrpcServer( + mockProm, + k8sAPI, + "linkerd", + "cluster.local", + []string{}, + ) + + k8sAPI.Sync(nil) + + return mockProm, fakeGrpcServer, nil +} + +func (exp expectedStatRPC) verifyPromQueries(mockProm *MockProm) error { + // if exp.expectedPrometheusQueries is an empty slice we still want to check no queries were executed. + if exp.expectedPrometheusQueries != nil { + sort.Strings(exp.expectedPrometheusQueries) + sort.Strings(mockProm.QueriesExecuted) + + // because reflect.DeepEqual([]string{}, nil) is false + if len(exp.expectedPrometheusQueries) == 0 && len(mockProm.QueriesExecuted) == 0 { + return nil + } + + if !reflect.DeepEqual(exp.expectedPrometheusQueries, mockProm.QueriesExecuted) { + return fmt.Errorf("Prometheus queries incorrect. \nExpected:\n%+v \nGot:\n%+v", + exp.expectedPrometheusQueries, mockProm.QueriesExecuted) + } + } + return nil +} diff --git a/controller/api/public/top_routes.go b/viz/metrics-api/top_routes.go similarity index 99% rename from controller/api/public/top_routes.go rename to viz/metrics-api/top_routes.go index 2eda83b399c5f..7028d9ba96a30 100644 --- a/controller/api/public/top_routes.go +++ b/viz/metrics-api/top_routes.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/controller/api/public/top_routes_test.go b/viz/metrics-api/top_routes_test.go similarity index 99% rename from controller/api/public/top_routes_test.go rename to viz/metrics-api/top_routes_test.go index 5bbfbf86c5540..a5a19608b7e7b 100644 --- a/controller/api/public/top_routes_test.go +++ b/viz/metrics-api/top_routes_test.go @@ -1,4 +1,4 @@ -package public +package api import ( "context" diff --git a/viz/metrics-api/util/api_utils.go b/viz/metrics-api/util/api_utils.go new file mode 100644 index 0000000000000..6c37cd4d80506 --- /dev/null +++ b/viz/metrics-api/util/api_utils.go @@ -0,0 +1,316 @@ +package util + +import ( + "errors" + "time" + + "github.com/linkerd/linkerd2/pkg/k8s" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + corev1 "k8s.io/api/core/v1" +) + +var ( + defaultMetricTimeWindow = "1m" + metricTimeWindowLowerBound = time.Second * 15 //the window value needs to equal or larger than that +) + +// StatsBaseRequestParams contains parameters that are used to build requests +// for metrics data. This includes requests to StatSummary and TopRoutes. +type StatsBaseRequestParams struct { + TimeWindow string + Namespace string + ResourceType string + ResourceName string + AllNamespaces bool +} + +// StatsSummaryRequestParams contains parameters that are used to build +// StatSummary requests. +type StatsSummaryRequestParams struct { + StatsBaseRequestParams + ToNamespace string + ToType string + ToName string + FromNamespace string + FromType string + FromName string + SkipStats bool + TCPStats bool + LabelSelector string +} + +// EdgesRequestParams contains parameters that are used to build +// Edges requests. +type EdgesRequestParams struct { + Namespace string + ResourceType string + AllNamespaces bool +} + +// TopRoutesRequestParams contains parameters that are used to build TopRoutes +// requests. +type TopRoutesRequestParams struct { + StatsBaseRequestParams + ToNamespace string + ToType string + ToName string + LabelSelector string +} + +// GatewayRequestParams contains parameters that are used to build a +// GatewayRequest +type GatewayRequestParams struct { + RemoteClusterName string + GatewayNamespace string + TimeWindow string +} + +// BuildStatSummaryRequest builds a Public API StatSummaryRequest from a +// StatsSummaryRequestParams. +func BuildStatSummaryRequest(p StatsSummaryRequestParams) (*pb.StatSummaryRequest, error) { + window := defaultMetricTimeWindow + if p.TimeWindow != "" { + w, err := time.ParseDuration(p.TimeWindow) + if err != nil { + return nil, err + } + + if w < metricTimeWindowLowerBound { + return nil, errors.New("metrics time window needs to be at least 15s") + } + + window = p.TimeWindow + } + + if p.AllNamespaces && p.ResourceName != "" { + return nil, errors.New("stats for a resource cannot be retrieved by name across all namespaces") + } + + targetNamespace := p.Namespace + if p.AllNamespaces { + targetNamespace = "" + } else if p.Namespace == "" { + targetNamespace = corev1.NamespaceDefault + } + + resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ResourceType) + if err != nil { + return nil, err + } + + statRequest := &pb.StatSummaryRequest{ + Selector: &pb.ResourceSelection{ + Resource: &pb.Resource{ + Namespace: targetNamespace, + Name: p.ResourceName, + Type: resourceType, + }, + LabelSelector: p.LabelSelector, + }, + TimeWindow: window, + SkipStats: p.SkipStats, + TcpStats: p.TCPStats, + } + + if p.ToName != "" || p.ToType != "" || p.ToNamespace != "" { + if p.ToNamespace == "" { + p.ToNamespace = targetNamespace + } + if p.ToType == "" { + p.ToType = resourceType + } + + toType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ToType) + if err != nil { + return nil, err + } + + toResource := pb.StatSummaryRequest_ToResource{ + ToResource: &pb.Resource{ + Namespace: p.ToNamespace, + Type: toType, + Name: p.ToName, + }, + } + statRequest.Outbound = &toResource + } + + if p.FromName != "" || p.FromType != "" || p.FromNamespace != "" { + if p.FromNamespace == "" { + p.FromNamespace = targetNamespace + } + if p.FromType == "" { + p.FromType = resourceType + } + + fromType, err := validateFromResourceType(p.FromType) + if err != nil { + return nil, err + } + + fromResource := pb.StatSummaryRequest_FromResource{ + FromResource: &pb.Resource{ + Namespace: p.FromNamespace, + Type: fromType, + Name: p.FromName, + }, + } + statRequest.Outbound = &fromResource + } + + return statRequest, nil +} + +// BuildEdgesRequest builds a Public API EdgesRequest from a +// EdgesRequestParams. +func BuildEdgesRequest(p EdgesRequestParams) (*pb.EdgesRequest, error) { + namespace := p.Namespace + + // If all namespaces was specified, ignore namespace value. + if p.AllNamespaces { + namespace = "" + } + + resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ResourceType) + if err != nil { + return nil, err + } + + edgesRequest := &pb.EdgesRequest{ + Selector: &pb.ResourceSelection{ + Resource: &pb.Resource{ + Namespace: namespace, + Type: resourceType, + }, + }, + } + + return edgesRequest, nil +} + +// BuildTopRoutesRequest builds a Public API TopRoutesRequest from a +// TopRoutesRequestParams. +func BuildTopRoutesRequest(p TopRoutesRequestParams) (*pb.TopRoutesRequest, error) { + window := defaultMetricTimeWindow + if p.TimeWindow != "" { + _, err := time.ParseDuration(p.TimeWindow) + if err != nil { + return nil, err + } + window = p.TimeWindow + } + + if p.AllNamespaces && p.ResourceName != "" { + return nil, errors.New("routes for a resource cannot be retrieved by name across all namespaces") + } + + targetNamespace := p.Namespace + if p.AllNamespaces { + targetNamespace = "" + } else if p.Namespace == "" { + targetNamespace = corev1.NamespaceDefault + } + + resourceType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ResourceType) + if err != nil { + return nil, err + } + + topRoutesRequest := &pb.TopRoutesRequest{ + Selector: &pb.ResourceSelection{ + Resource: &pb.Resource{ + Namespace: targetNamespace, + Name: p.ResourceName, + Type: resourceType, + }, + LabelSelector: p.LabelSelector, + }, + TimeWindow: window, + } + + if p.ToName != "" || p.ToType != "" || p.ToNamespace != "" { + if p.ToNamespace == "" { + p.ToNamespace = targetNamespace + } + if p.ToType == "" { + p.ToType = resourceType + } + + toType, err := k8s.CanonicalResourceNameFromFriendlyName(p.ToType) + if err != nil { + return nil, err + } + + toResource := pb.TopRoutesRequest_ToResource{ + ToResource: &pb.Resource{ + Namespace: p.ToNamespace, + Type: toType, + Name: p.ToName, + }, + } + topRoutesRequest.Outbound = &toResource + } else { + topRoutesRequest.Outbound = &pb.TopRoutesRequest_None{ + None: &pb.Empty{}, + } + } + + return topRoutesRequest, nil +} + +// An authority can only receive traffic, not send it, so it can't be a --from +func validateFromResourceType(resourceType string) (string, error) { + name, err := k8s.CanonicalResourceNameFromFriendlyName(resourceType) + if err != nil { + return "", err + } + if name == k8s.Authority { + return "", errors.New("cannot query traffic --from an authority") + } + return name, nil +} + +// K8sPodToPublicPod converts a Kubernetes Pod to a Public API Pod +func K8sPodToPublicPod(pod corev1.Pod, ownerKind string, ownerName string) *pb.Pod { + status := string(pod.Status.Phase) + if pod.DeletionTimestamp != nil { + status = "Terminating" + } + + if pod.Status.Reason == "Evicted" { + status = "Evicted" + } + + controllerComponent := pod.Labels[k8s.ControllerComponentLabel] + controllerNS := pod.Labels[k8s.ControllerNSLabel] + + item := &pb.Pod{ + Name: pod.Namespace + "/" + pod.Name, + Status: status, + PodIP: pod.Status.PodIP, + ControllerNamespace: controllerNS, + ControlPlane: controllerComponent != "", + ProxyReady: k8s.GetProxyReady(pod), + ProxyVersion: k8s.GetProxyVersion(pod), + ResourceVersion: pod.ResourceVersion, + } + + namespacedOwnerName := pod.Namespace + "/" + ownerName + + switch ownerKind { + case k8s.Deployment: + item.Owner = &pb.Pod_Deployment{Deployment: namespacedOwnerName} + case k8s.DaemonSet: + item.Owner = &pb.Pod_DaemonSet{DaemonSet: namespacedOwnerName} + case k8s.Job: + item.Owner = &pb.Pod_Job{Job: namespacedOwnerName} + case k8s.ReplicaSet: + item.Owner = &pb.Pod_ReplicaSet{ReplicaSet: namespacedOwnerName} + case k8s.ReplicationController: + item.Owner = &pb.Pod_ReplicationController{ReplicationController: namespacedOwnerName} + case k8s.StatefulSet: + item.Owner = &pb.Pod_StatefulSet{StatefulSet: namespacedOwnerName} + } + + return item +} diff --git a/viz/metrics-api/util/api_utils_test.go b/viz/metrics-api/util/api_utils_test.go new file mode 100644 index 0000000000000..3accfcc730b38 --- /dev/null +++ b/viz/metrics-api/util/api_utils_test.go @@ -0,0 +1,253 @@ +package util + +import ( + "reflect" + "testing" + + "github.com/linkerd/linkerd2/pkg/k8s" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestBuildStatSummaryRequest(t *testing.T) { + t.Run("Maps Kubernetes friendly names to canonical names", func(t *testing.T) { + expectations := map[string]string{ + "deployments": k8s.Deployment, + "deployment": k8s.Deployment, + "deploy": k8s.Deployment, + "pods": k8s.Pod, + "pod": k8s.Pod, + "po": k8s.Pod, + } + + for friendly, canonical := range expectations { + statSummaryRequest, err := BuildStatSummaryRequest( + StatsSummaryRequestParams{ + StatsBaseRequestParams: StatsBaseRequestParams{ + ResourceType: friendly, + }, + }, + ) + if err != nil { + t.Fatalf("Unexpected error from BuildStatSummaryRequest [%s => %s]: %s", friendly, canonical, err) + } + if statSummaryRequest.Selector.Resource.Type != canonical { + t.Fatalf("Unexpected resource type from BuildStatSummaryRequest [%s => %s]: %s", friendly, canonical, statSummaryRequest.Selector.Resource.Type) + } + } + }) + + t.Run("Parses valid time windows", func(t *testing.T) { + expectations := []string{ + "1m", + "60s", + "1m", + } + + for _, timeWindow := range expectations { + statSummaryRequest, err := BuildStatSummaryRequest( + StatsSummaryRequestParams{ + StatsBaseRequestParams: StatsBaseRequestParams{ + TimeWindow: timeWindow, + ResourceType: k8s.Deployment, + }, + }, + ) + if err != nil { + t.Fatalf("Unexpected error from BuildStatSummaryRequest [%s => %s]", timeWindow, err) + } + if statSummaryRequest.TimeWindow != timeWindow { + t.Fatalf("Unexpected TimeWindow from BuildStatSummaryRequest [%s => %s]", timeWindow, statSummaryRequest.TimeWindow) + } + } + }) + + t.Run("Rejects invalid time windows", func(t *testing.T) { + expectations := map[string]string{ + "1": "time: missing unit in duration 1", + "s": "time: invalid duration s", + } + + for timeWindow, msg := range expectations { + _, err := BuildStatSummaryRequest( + StatsSummaryRequestParams{ + StatsBaseRequestParams: StatsBaseRequestParams{ + TimeWindow: timeWindow, + }, + }, + ) + if err == nil { + t.Fatalf("BuildStatSummaryRequest(%s) unexpectedly succeeded, should have returned %s", timeWindow, msg) + } + if err.Error() != msg { + t.Fatalf("BuildStatSummaryRequest(%s) should have returned: %s but got unexpected message: %s", timeWindow, msg, err) + } + } + }) + + t.Run("Rejects invalid Kubernetes resource types", func(t *testing.T) { + expectations := map[string]string{ + "foo": "cannot find Kubernetes canonical name from friendly name [foo]", + "": "cannot find Kubernetes canonical name from friendly name []", + } + + for input, msg := range expectations { + _, err := BuildStatSummaryRequest( + StatsSummaryRequestParams{ + StatsBaseRequestParams: StatsBaseRequestParams{ + ResourceType: input, + }, + }, + ) + if err == nil { + t.Fatalf("BuildStatSummaryRequest(%s) unexpectedly succeeded, should have returned %s", input, msg) + } + if err.Error() != msg { + t.Fatalf("BuildStatSummaryRequest(%s) should have returned: %s but got unexpected message: %s", input, msg, err) + } + } + }) +} + +func TestBuildTopRoutesRequest(t *testing.T) { + t.Run("Parses valid time windows", func(t *testing.T) { + expectations := []string{ + "1m", + "60s", + "1m", + } + + for _, timeWindow := range expectations { + topRoutesRequest, err := BuildTopRoutesRequest( + TopRoutesRequestParams{ + StatsBaseRequestParams: StatsBaseRequestParams{ + TimeWindow: timeWindow, + ResourceType: k8s.Deployment, + }, + }, + ) + if err != nil { + t.Fatalf("Unexpected error from BuildTopRoutesRequest [%s => %s]", timeWindow, err) + } + if topRoutesRequest.TimeWindow != timeWindow { + t.Fatalf("Unexpected TimeWindow from BuildTopRoutesRequest [%s => %s]", timeWindow, topRoutesRequest.TimeWindow) + } + } + }) + + t.Run("Rejects invalid time windows", func(t *testing.T) { + expectations := map[string]string{ + "1": "time: missing unit in duration 1", + "s": "time: invalid duration s", + } + + for timeWindow, msg := range expectations { + _, err := BuildTopRoutesRequest( + TopRoutesRequestParams{ + StatsBaseRequestParams: StatsBaseRequestParams{ + TimeWindow: timeWindow, + ResourceType: k8s.Deployment, + }, + }, + ) + if err == nil { + t.Fatalf("BuildTopRoutesRequest(%s) unexpectedly succeeded, should have returned %s", timeWindow, msg) + } + if err.Error() != msg { + t.Fatalf("BuildTopRoutesRequest(%s) should have returned: %s but got unexpected message: %s", timeWindow, msg, err) + } + } + }) +} + +func TestK8sPodToPublicPod(t *testing.T) { + type podExp struct { + k8sPod corev1.Pod + ownerKind string + ownerName string + publicPod *pb.Pod + } + + t.Run("Returns expected pods", func(t *testing.T) { + expectations := []podExp{ + { + k8sPod: corev1.Pod{}, + publicPod: &pb.Pod{ + Name: "/", + }, + }, + { + k8sPod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "name", + ResourceVersion: "resource-version", + Labels: map[string]string{ + k8s.ControllerComponentLabel: "controller-component", + k8s.ControllerNSLabel: "controller-ns", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: k8s.ProxyContainerName, + Image: "linkerd-proxy:test-version", + }, + }, + }, + Status: corev1.PodStatus{ + PodIP: "pod-ip", + Phase: "status", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + ownerKind: k8s.Deployment, + ownerName: "owner-name", + publicPod: &pb.Pod{ + Name: "ns/name", + Owner: &pb.Pod_Deployment{Deployment: "ns/owner-name"}, + ResourceVersion: "resource-version", + ControlPlane: true, + ControllerNamespace: "controller-ns", + Status: "status", + ProxyReady: true, + ProxyVersion: "test-version", + PodIP: "pod-ip", + }, + }, + { + k8sPod: corev1.Pod{ + Status: corev1.PodStatus{ + Phase: "Failed", + Reason: "Evicted", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: k8s.ProxyContainerName, + Ready: true, + }, + }, + }, + }, + ownerName: "owner-name", + publicPod: &pb.Pod{ + Name: "/", + Status: "Evicted", + ProxyReady: true, + }, + }, + } + + for _, exp := range expectations { + res := K8sPodToPublicPod(exp.k8sPod, exp.ownerKind, exp.ownerName) + if !reflect.DeepEqual(exp.publicPod, res) { + t.Fatalf("Expected pod to be [%+v] but was [%+v]", exp.publicPod, res) + } + } + }) +} diff --git a/viz/pkg/api/api.go b/viz/pkg/api/api.go new file mode 100644 index 0000000000000..cf4d86b07e3ba --- /dev/null +++ b/viz/pkg/api/api.go @@ -0,0 +1,62 @@ +package api + +import ( + "fmt" + "os" + "time" + + "github.com/linkerd/linkerd2/pkg/healthcheck" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + vizHealthCheck "github.com/linkerd/linkerd2/viz/pkg/healthcheck" +) + +// CheckClientOrExit builds a new Viz API client and executes default status +// checks to determine if the client can successfully perform cli commands. If the +// checks fail, then CLI will print an error and exit. +func CheckClientOrExit(hcOptions healthcheck.Options) pb.ApiClient { + hcOptions.RetryDeadline = time.Time{} + return CheckClientOrRetryOrExit(hcOptions, false) +} + +// CheckClientOrRetryOrExit builds a new Viz API client and executes status +// checks to determine if the client can successfully connect to the API. If the +// checks fail, then CLI will print an error and exit. If the hcOptions.retryDeadline +// param is specified, then the CLI will print a message to stderr and retry. +func CheckClientOrRetryOrExit(hcOptions healthcheck.Options, apiChecks bool) pb.ApiClient { + checks := []healthcheck.CategoryID{ + healthcheck.KubernetesAPIChecks, + vizHealthCheck.LinkerdVizExtensionCheck, + } + + if apiChecks { + checks = append(checks, healthcheck.LinkerdAPIChecks) + } + + hc := vizHealthCheck.NewHealthChecker(checks, &hcOptions) + + hc.RunChecks(exitOnError) + return hc.VizAPIClient() +} + +func exitOnError(result *healthcheck.CheckResult) { + if result.Retry { + fmt.Fprintln(os.Stderr, "Waiting for control plane to become available") + return + } + + if result.Err != nil && !result.Warning { + var msg string + switch result.Category { + case healthcheck.KubernetesAPIChecks: + msg = "Cannot connect to Kubernetes" + case vizHealthCheck.LinkerdVizExtensionCheck: + msg = "Cannot connect to Linkerd Viz" + } + fmt.Fprintf(os.Stderr, "%s: %s\n", msg, result.Err) + + checkCmd := "linkerd viz check" + fmt.Fprintf(os.Stderr, "Validate the install with: %s\n", checkCmd) + + os.Exit(1) + } +} diff --git a/viz/pkg/healthcheck/healthcheck.go b/viz/pkg/healthcheck/healthcheck.go new file mode 100644 index 0000000000000..d49bdc79b8472 --- /dev/null +++ b/viz/pkg/healthcheck/healthcheck.go @@ -0,0 +1,216 @@ +package healthcheck + +import ( + "context" + "crypto/x509" + "fmt" + + healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck" + "github.com/linkerd/linkerd2/pkg/healthcheck" + "github.com/linkerd/linkerd2/pkg/k8s" + "github.com/linkerd/linkerd2/pkg/tls" + "github.com/linkerd/linkerd2/viz/metrics-api/client" + pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apiregistrationv1client "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1" +) + +const ( + // LinkerdVizExtensionCheck adds checks related to the Linkerd Viz extension + LinkerdVizExtensionCheck healthcheck.CategoryID = "linkerd-viz" + + tapTLSSecretName = "linkerd-tap-k8s-tls" + tapOldTLSSecretName = "linkerd-tap-tls" + + // linkerdTapAPIServiceName is the name of the tap api service + // This key is passed to checkApiService method to check whether + // the api service is available or not + linkerdTapAPIServiceName = "v1alpha1.tap.linkerd.io" +) + +// HealthChecker wraps Linkerd's main healthchecker, adding extra fields for Viz +type HealthChecker struct { + *healthcheck.HealthChecker + vizNamespace string + vizAPIClient pb.ApiClient +} + +// NewHealthChecker returns an initialized HealthChecker for Viz +func NewHealthChecker(categoryIDs []healthcheck.CategoryID, options *healthcheck.Options) *HealthChecker { + parentHC := healthcheck.NewHealthChecker(categoryIDs, options) + hc := &HealthChecker{HealthChecker: parentHC} + parentHC.AppendCategories(*hc.vizCategory()) + return hc +} + +// VizAPIClient returns a fully configured Viz API client +func (hc *HealthChecker) VizAPIClient() pb.ApiClient { + return hc.vizAPIClient +} + +// RunChecks implements the healthcheck.Runner interface +func (hc *HealthChecker) RunChecks(observer healthcheck.CheckObserver) bool { + return hc.HealthChecker.RunChecks(observer) +} + +func (hc *HealthChecker) vizCategory() *healthcheck.Category { + checkers := []healthcheck.Checker{} + checkers = append(checkers, + *healthcheck.NewChecker("linkerd-viz Namespace exists"). + WithHintAnchor("l5d-viz-ns-exists"). + Fatal(). + WithCheck(func(ctx context.Context) error { + vizNs, err := hc.KubeAPIClient().GetNamespaceWithExtensionLabel(ctx, "linkerd-viz") + if err == nil { + hc.vizNamespace = vizNs.Name + } + return err + })) + + checkers = append(checkers, + *healthcheck.NewChecker("linkerd-viz ClusterRoles exist"). + WithHintAnchor("l5d-viz-cr-exists"). + Fatal(). + Warning(). + WithCheck(func(ctx context.Context) error { + return healthcheck.CheckClusterRoles(ctx, hc.KubeAPIClient(), true, []string{fmt.Sprintf("linkerd-%s-prometheus", hc.vizNamespace), fmt.Sprintf("linkerd-%s-tap", hc.vizNamespace)}, "") + })) + + checkers = append(checkers, + *healthcheck.NewChecker("linkerd-viz ClusterRoleBindings exist"). + WithHintAnchor("l5d-viz-crb-exists"). + Fatal(). + Warning(). + WithCheck(func(ctx context.Context) error { + return healthcheck.CheckClusterRoleBindings(ctx, hc.KubeAPIClient(), true, []string{fmt.Sprintf("linkerd-%s-prometheus", hc.vizNamespace), fmt.Sprintf("linkerd-%s-tap", hc.vizNamespace)}, "") + })) + + checkers = append(checkers, + *healthcheck.NewChecker("linkerd-viz ConfigMaps exist"). + WithHintAnchor("l5d-viz-cm-exists"). + Fatal(). + Warning(). + WithCheck(func(ctx context.Context) error { + return healthcheck.CheckConfigMaps(ctx, hc.KubeAPIClient(), hc.vizNamespace, true, []string{"linkerd-prometheus-config", "linkerd-grafana-config"}, "") + })) + + checkers = append(checkers, + *healthcheck.NewChecker("tap API server has valid cert"). + WithHintAnchor("l5d-tap-cert-valid"). + Fatal(). + WithCheck(func(ctx context.Context) error { + anchors, err := fetchTapCaBundle(ctx, hc.KubeAPIClient()) + if err != nil { + return err + } + cert, err := hc.FetchCredsFromSecret(ctx, hc.vizNamespace, tapTLSSecretName) + if kerrors.IsNotFound(err) { + cert, err = hc.FetchCredsFromOldSecret(ctx, hc.vizNamespace, tapOldTLSSecretName) + } + if err != nil { + return err + } + + identityName := fmt.Sprintf("linkerd-tap.%s.svc", hc.vizNamespace) + return hc.CheckCertAndAnchors(cert, anchors, identityName) + })) + + checkers = append(checkers, + *healthcheck.NewChecker("tap API server cert is valid for at least 60 days"). + WithHintAnchor("l5d-webhook-cert-not-expiring-soon"). + Warning(). + WithCheck(func(ctx context.Context) error { + cert, err := hc.FetchCredsFromSecret(ctx, hc.vizNamespace, tapTLSSecretName) + if kerrors.IsNotFound(err) { + cert, err = hc.FetchCredsFromOldSecret(ctx, hc.vizNamespace, tapOldTLSSecretName) + } + if err != nil { + return err + } + return hc.CheckCertAndAnchorsExpiringSoon(cert) + })) + + checkers = append(checkers, + *healthcheck.NewChecker("tap API service is running"). + WithHintAnchor("l5d-tap-api"). + Warning(). + WithRetryDeadline(hc.RetryDeadline). + WithCheck(func(ctx context.Context) error { + return hc.CheckAPIService(ctx, linkerdTapAPIServiceName) + })) + + checkers = append(checkers, + *healthcheck.NewChecker("viz extension pods are running"). + WithHintAnchor("l5d-viz-pods-running"). + Warning(). + WithRetryDeadline(hc.RetryDeadline). + SurfaceErrorOnRetry(). + WithCheck(func(ctx context.Context) error { + pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, hc.vizNamespace) + if err != nil { + return err + } + + // Check for relevant pods to be present + err = healthcheck.CheckForPods(pods, []string{"linkerd-grafana", "linkerd-prometheus", "linkerd-web", "linkerd-tap"}) + if err != nil { + return err + } + + return healthcheck.CheckPodsRunning(pods, "") + })) + + checkers = append(checkers, + *healthcheck.NewChecker("linkerd-viz pods are injected"). + WithHintAnchor("l5d-viz-pods-injection"). + Warning(). + WithCheck(func(ctx context.Context) error { + pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, hc.vizNamespace) + if err != nil { + return err + } + return healthcheck.CheckIfDataPlanePodsExist(pods) + })) + + checkers = append(checkers, + *healthcheck.NewChecker("can initialize the client"). + WithHintAnchor("l5d-viz-existence-client"). + Fatal(). + WithCheck(func(ctx context.Context) (err error) { + hc.vizAPIClient, err = client.NewExternalClient(ctx, hc.vizNamespace, hc.KubeAPIClient()) + return + })) + + checkers = append(checkers, + *healthcheck.NewChecker("viz extension self-check"). + WithHintAnchor("l5d-api-control-api"). + Fatal(). + // to avoid confusing users with a prometheus readiness error, we only show + // "waiting for check to complete" while things converge. If after the timeout + // it still hasn't converged, we show the real error (a 503 usually). + WithRetryDeadline(hc.RetryDeadline). + WithCheckRPC(func(ctx context.Context) (*healthcheckPb.SelfCheckResponse, error) { + return hc.vizAPIClient.SelfCheck(ctx, &healthcheckPb.SelfCheckRequest{}) + })) + + return healthcheck.NewCategory(LinkerdVizExtensionCheck, checkers, true) +} + +func fetchTapCaBundle(ctx context.Context, kubeAPI *k8s.KubernetesAPI) ([]*x509.Certificate, error) { + apiServiceClient, err := apiregistrationv1client.NewForConfig(kubeAPI.Config) + if err != nil { + return nil, err + } + + apiService, err := apiServiceClient.APIServices().Get(ctx, linkerdTapAPIServiceName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + caBundle, err := tls.DecodePEMCertificates(string(apiService.Spec.CABundle)) + if err != nil { + return nil, err + } + return caBundle, nil +} diff --git a/web/Dockerfile b/web/Dockerfile index 825e8e5ba93da..21379975c2c73 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -38,7 +38,8 @@ RUN mkdir -p web COPY web/main.go web COPY web/srv web/srv COPY controller controller -COPY viz/metrics-api/gen/viz viz/metrics-api/gen/viz +COPY viz/metrics-api viz/metrics-api +COPY viz/pkg viz/pkg COPY pkg pkg ARG TARGETARCH RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -mod=readonly -o web/web -ldflags "-s -w" ./web diff --git a/web/main.go b/web/main.go index a6f8d1c340cd5..7b6376d4ed189 100644 --- a/web/main.go +++ b/web/main.go @@ -16,6 +16,7 @@ import ( "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/trace" + "github.com/linkerd/linkerd2/viz/metrics-api/client" "github.com/linkerd/linkerd2/web/srv" log "github.com/sirupsen/logrus" ) @@ -25,13 +26,15 @@ func main() { addr := cmd.String("addr", ":8084", "address to serve on") metricsAddr := cmd.String("metrics-addr", ":9994", "address to serve scrapable metrics on") - apiAddr := cmd.String("api-addr", "127.0.0.1:8085", "address of the linkerd-controller-api service") + publicAPIAddr := cmd.String("linkerd-controller-api-addr", "127.0.0.1:8085", "address of the linkerd-controller-api service") + vizAPIAddr := cmd.String("linkerd-metrics-api-addr", "127.0.0.1:8085", "address of the linkerd-metrics-api service") grafanaAddr := cmd.String("grafana-addr", "", "address of the linkerd-grafana service") jaegerAddr := cmd.String("jaeger-addr", "", "address of the jaeger service") templateDir := cmd.String("template-dir", "templates", "directory to search for template files") staticDir := cmd.String("static-dir", "app/dist", "directory to search for static files") reload := cmd.Bool("reload", true, "reloading set to true or false") controllerNamespace := cmd.String("controller-namespace", "linkerd", "namespace in which Linkerd is installed") + vizNamespace := cmd.String("viz-namespace", "linkerd", "namespace in which Linkerd viz is installed") enforcedHost := cmd.String("enforced-host", "", "regexp describing the allowed values for the Host header; protects from DNS-rebinding attacks") kubeConfigPath := cmd.String("kubeconfig", "", "path to kube config") clusterDomain := cmd.String("cluster-domain", "", "kubernetes cluster domain") @@ -41,17 +44,22 @@ func main() { flags.ConfigureAndParse(cmd, os.Args[1:]) ctx := context.Background() - _, _, err := net.SplitHostPort(*apiAddr) // Verify apiAddr is of the form host:port. + _, _, err := net.SplitHostPort(*vizAPIAddr) // Verify vizAPIAddr is of the form host:port. if err != nil { - log.Fatalf("failed to parse API server address: %s", *apiAddr) + log.Fatalf("failed to parse metrics API server address: %s", *vizAPIAddr) } - client, err := public.NewInternalClient(*controllerNamespace, *apiAddr) + client, err := client.NewInternalClient(*vizNamespace, *vizAPIAddr) if err != nil { - log.Fatalf("failed to construct client for viz API server URL %s", *apiAddr) + log.Fatalf("failed to construct client for viz API server URL %s", *vizAPIAddr) } - publicClient, err := public.NewInternalPublicClient(*controllerNamespace, *apiAddr) + + _, _, err = net.SplitHostPort(*publicAPIAddr) // Verify publicAPIAddr is of the form host:port. + if err != nil { + log.Fatalf("failed to parse public API server address: %s", *publicAPIAddr) + } + publicClient, err := public.NewInternalClient(*controllerNamespace, *publicAPIAddr) if err != nil { - log.Fatalf("failed to construct client for public API server URL %s", *apiAddr) + log.Fatalf("failed to construct client for public API server URL %s", *publicAPIAddr) } if *clusterDomain == "" { @@ -77,7 +85,7 @@ func main() { hc := healthcheck.NewHealthChecker(checks, &healthcheck.Options{ ControlPlaneNamespace: *controllerNamespace, KubeConfig: *kubeConfigPath, - APIAddr: *apiAddr, + APIAddr: *publicAPIAddr, }) cm, _, err := healthcheck.FetchLinkerdConfigMap(ctx, k8sAPI, *controllerNamespace) diff --git a/web/srv/api_handlers.go b/web/srv/api_handlers.go index 6e46105eeebaf..437ce408ed44a 100644 --- a/web/srv/api_handlers.go +++ b/web/srv/api_handlers.go @@ -15,13 +15,14 @@ import ( "github.com/golang/protobuf/proto" "github.com/gorilla/websocket" "github.com/julienschmidt/httprouter" - "github.com/linkerd/linkerd2/controller/api/util" + coreUtil "github.com/linkerd/linkerd2/controller/api/util" publicPb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/protohttp" "github.com/linkerd/linkerd2/pkg/tap" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" + vizUtil "github.com/linkerd/linkerd2/viz/metrics-api/util" log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" @@ -137,8 +138,8 @@ func (h *handler) handleAPIStat(w http.ResponseWriter, req *http.Request, p http trueStr := fmt.Sprintf("%t", true) - requestParams := util.StatsSummaryRequestParams{ - StatsBaseRequestParams: util.StatsBaseRequestParams{ + requestParams := vizUtil.StatsSummaryRequestParams{ + StatsBaseRequestParams: vizUtil.StatsBaseRequestParams{ TimeWindow: req.FormValue("window"), ResourceName: req.FormValue("resource_name"), ResourceType: req.FormValue("resource_type"), @@ -160,7 +161,7 @@ func (h *handler) handleAPIStat(w http.ResponseWriter, req *http.Request, p http requestParams.ResourceType = defaultResourceType } - statRequest, err := util.BuildStatSummaryRequest(requestParams) + statRequest, err := vizUtil.BuildStatSummaryRequest(requestParams) if err != nil { renderJSONError(w, err, http.StatusInternalServerError) return @@ -184,8 +185,8 @@ func (h *handler) handleAPIStat(w http.ResponseWriter, req *http.Request, p http } func (h *handler) handleAPITopRoutes(w http.ResponseWriter, req *http.Request, p httprouter.Params) { - requestParams := util.TopRoutesRequestParams{ - StatsBaseRequestParams: util.StatsBaseRequestParams{ + requestParams := vizUtil.TopRoutesRequestParams{ + StatsBaseRequestParams: vizUtil.StatsBaseRequestParams{ TimeWindow: req.FormValue("window"), ResourceName: req.FormValue("resource_name"), ResourceType: req.FormValue("resource_type"), @@ -196,7 +197,7 @@ func (h *handler) handleAPITopRoutes(w http.ResponseWriter, req *http.Request, p ToNamespace: req.FormValue("to_namespace"), } - topReq, err := util.BuildTopRoutesRequest(requestParams) + topReq, err := vizUtil.BuildTopRoutesRequest(requestParams) if err != nil { renderJSONError(w, err, http.StatusBadRequest) return @@ -256,14 +257,14 @@ func (h *handler) handleAPITap(w http.ResponseWriter, req *http.Request, p httpr return } - var requestParams util.TapRequestParams + var requestParams coreUtil.TapRequestParams err = json.Unmarshal(message, &requestParams) if err != nil { websocketError(ws, websocket.CloseInternalServerErr, err) return } - tapReq, err := util.BuildTapByResourceRequest(requestParams) + tapReq, err := coreUtil.BuildTapByResourceRequest(requestParams) if err != nil { websocketError(ws, websocket.CloseInternalServerErr, err) return @@ -328,12 +329,12 @@ func (h *handler) handleAPITap(w http.ResponseWriter, req *http.Request, p httpr } func (h *handler) handleAPIEdges(w http.ResponseWriter, req *http.Request, p httprouter.Params) { - requestParams := util.EdgesRequestParams{ + requestParams := vizUtil.EdgesRequestParams{ Namespace: req.FormValue("namespace"), ResourceType: req.FormValue("resource_type"), } - edgesRequest, err := util.BuildEdgesRequest(requestParams) + edgesRequest, err := vizUtil.BuildEdgesRequest(requestParams) if err != nil { renderJSONError(w, err, http.StatusInternalServerError) return diff --git a/web/srv/api_handlers_test.go b/web/srv/api_handlers_test.go index 57cd4d5215d76..d67ec01487443 100644 --- a/web/srv/api_handlers_test.go +++ b/web/srv/api_handlers_test.go @@ -15,6 +15,7 @@ import ( "github.com/linkerd/linkerd2/controller/api/public" publicPb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/pkg/healthcheck" + vizApi "github.com/linkerd/linkerd2/viz/metrics-api" pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" ) @@ -142,7 +143,7 @@ func TestHandleApiCheck(t *testing.T) { } func TestHandleApiGateway(t *testing.T) { - mockAPIClient := &public.MockAPIClient{ + mockAPIClient := &vizApi.MockAPIClient{ GatewaysResponseToReturn: &pb.GatewaysResponse{ Response: &pb.GatewaysResponse_Ok_{ Ok: &pb.GatewaysResponse_Ok{ diff --git a/web/srv/handlers.go b/web/srv/handlers.go index 836e858640668..b6c462ed76eaa 100644 --- a/web/srv/handlers.go +++ b/web/srv/handlers.go @@ -11,6 +11,7 @@ import ( pb "github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/pkg/k8s" profiles "github.com/linkerd/linkerd2/pkg/profiles" + vizPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "github.com/patrickmn/go-cache" log "github.com/sirupsen/logrus" ) @@ -22,8 +23,8 @@ type ( handler struct { render renderTemplate - apiClient public.VizAPIClient - publicAPIClient public.PublicAPIClient + apiClient vizPb.ApiClient + publicAPIClient public.Client k8sAPI *k8s.KubernetesAPI uuid string controllerNamespace string diff --git a/web/srv/handlers_test.go b/web/srv/handlers_test.go index 64e650db74962..a9d937640b975 100644 --- a/web/srv/handlers_test.go +++ b/web/srv/handlers_test.go @@ -76,7 +76,7 @@ func TestHandleConfigDownload(t *testing.T) { handler := &handler{ render: server.RenderTemplate, - apiClient: mockAPIClient, + publicAPIClient: mockAPIClient, controllerNamespace: "linkerd", clusterDomain: "mycluster.local", } diff --git a/web/srv/server.go b/web/srv/server.go index 85f7a47a22e7b..74d7041ff8a40 100644 --- a/web/srv/server.go +++ b/web/srv/server.go @@ -17,6 +17,7 @@ import ( "github.com/linkerd/linkerd2/pkg/healthcheck" "github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/prometheus" + vizPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz" "github.com/patrickmn/go-cache" log "github.com/sirupsen/logrus" ) @@ -93,8 +94,8 @@ func NewServer( clusterDomain string, reload bool, reHost *regexp.Regexp, - apiClient public.VizAPIClient, - publicAPIClient public.PublicAPIClient, + apiClient vizPb.ApiClient, + publicAPIClient public.Client, k8sAPI *k8s.KubernetesAPI, hc healthChecker, ) *http.Server {