mirror of https://github.com/linkerd/linkerd2.git
				
				
				
			viz: Prohibit authority resource targets in stat commands (#13578)
There are plans to remove the authority label in inbound proxy metrics. When that happens we would not longer be able to use the viz stat/top commands to query by `authority`. This is a change to disable being able to invoke these commands with an `authority` resource target. Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
This commit is contained in:
		
							parent
							
								
									a726757fb1
								
							
						
					
					
						commit
						31a580683e
					
				| 
						 | 
				
			
			@ -12,7 +12,6 @@ import (
 | 
			
		|||
// These constants are string representations of Kubernetes resource types.
 | 
			
		||||
const (
 | 
			
		||||
	All                   = "all"
 | 
			
		||||
	Authority             = "authority"
 | 
			
		||||
	ConfigMap             = "configmap"
 | 
			
		||||
	CronJob               = "cronjob"
 | 
			
		||||
	DaemonSet             = "daemonset"
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +72,6 @@ type resourceName struct {
 | 
			
		|||
 | 
			
		||||
// AllResources is a sorted list of all resources defined as constants above.
 | 
			
		||||
var AllResources = []string{
 | 
			
		||||
	Authority,
 | 
			
		||||
	AuthorizationPolicy,
 | 
			
		||||
	CronJob,
 | 
			
		||||
	DaemonSet,
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +98,6 @@ var StatAllResourceTypes = []string{
 | 
			
		|||
	ReplicationController,
 | 
			
		||||
	Pod,
 | 
			
		||||
	Service,
 | 
			
		||||
	Authority,
 | 
			
		||||
	CronJob,
 | 
			
		||||
	ReplicaSet,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -115,13 +112,11 @@ var CompletionResourceTypes = []string{
 | 
			
		|||
	ReplicationController,
 | 
			
		||||
	Pod,
 | 
			
		||||
	Service,
 | 
			
		||||
	Authority,
 | 
			
		||||
	CronJob,
 | 
			
		||||
	ReplicaSet,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var resourceNames = []resourceName{
 | 
			
		||||
	{"au", "authority", "authorities"},
 | 
			
		||||
	{"cj", "cronjob", "cronjobs"},
 | 
			
		||||
	{"ds", "daemonset", "daemonsets"},
 | 
			
		||||
	{"deploy", "deployment", "deployments"},
 | 
			
		||||
| 
						 | 
				
			
			@ -190,8 +185,6 @@ func PluralResourceNameFromFriendlyName(friendlyName string) (string, error) {
 | 
			
		|||
// Essentially the reverse of CanonicalResourceNameFromFriendlyName
 | 
			
		||||
func ShortNameFromCanonicalResourceName(canonicalName string) string {
 | 
			
		||||
	switch canonicalName {
 | 
			
		||||
	case Authority:
 | 
			
		||||
		return "au"
 | 
			
		||||
	case CronJob:
 | 
			
		||||
		return "cj"
 | 
			
		||||
	case DaemonSet:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,8 +32,6 @@ func TestCanonicalResourceNameFromFriendlyName(t *testing.T) {
 | 
			
		|||
			"pod":          Pod,
 | 
			
		||||
			"deployment":   Deployment,
 | 
			
		||||
			"deployments":  Deployment,
 | 
			
		||||
			"au":           Authority,
 | 
			
		||||
			"authorities":  Authority,
 | 
			
		||||
			"cj":           CronJob,
 | 
			
		||||
			"cronjob":      CronJob,
 | 
			
		||||
			"serverauthz":  ServerAuthorization,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@ import (
 | 
			
		|||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +36,7 @@ func TestMain(m *testing.M) {
 | 
			
		|||
// requesting, and the test will pass.
 | 
			
		||||
func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	var prometheusPod, prometheusAuthority, prometheusNamespace, prometheusDeployment, metricsPod string
 | 
			
		||||
	var prometheusPod, prometheusNamespace, prometheusDeployment, metricsPod string
 | 
			
		||||
	// Get Metrics Pod
 | 
			
		||||
	pods, err := TestHelper.GetPodNamesForDeployment(ctx, TestHelper.GetVizNamespace(), "metrics-api")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,13 +61,11 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
		testutil.Fatalf(t, "expected 1 pod for prometheus, got %d", len(pods))
 | 
			
		||||
	}
 | 
			
		||||
	prometheusPod = pods[0]
 | 
			
		||||
	prometheusAuthority = prometheusDeployment + "." + prometheusNamespace + ".svc.cluster.local:9090"
 | 
			
		||||
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		args         []string
 | 
			
		||||
		expectedRows map[string]string
 | 
			
		||||
		status       string
 | 
			
		||||
		isAuthority  bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetLinkerdNamespace()},
 | 
			
		||||
| 
						 | 
				
			
			@ -103,27 +100,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
				"metrics-api": "1/1",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "po", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("au/%s", prometheusAuthority), "--to-namespace", prometheusNamespace},
 | 
			
		||||
			expectedRows: map[string]string{
 | 
			
		||||
				metricsPod: "1/1",
 | 
			
		||||
			},
 | 
			
		||||
			status: "Running",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
 | 
			
		||||
			expectedRows: map[string]string{
 | 
			
		||||
				prometheusAuthority: "-",
 | 
			
		||||
			},
 | 
			
		||||
			isAuthority: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
 | 
			
		||||
			expectedRows: map[string]string{
 | 
			
		||||
				prometheusAuthority: "-",
 | 
			
		||||
			},
 | 
			
		||||
			isAuthority: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !TestHelper.ExternalPrometheus() {
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +107,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
			args         []string
 | 
			
		||||
			expectedRows map[string]string
 | 
			
		||||
			status       string
 | 
			
		||||
			isAuthority  bool
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +137,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
			args         []string
 | 
			
		||||
			expectedRows map[string]string
 | 
			
		||||
			status       string
 | 
			
		||||
			isAuthority  bool
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +186,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
			args         []string
 | 
			
		||||
			expectedRows map[string]string
 | 
			
		||||
			status       string
 | 
			
		||||
			isAuthority  bool
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				args: []string{"viz", "stat", "svc", "-n", prefixedNs},
 | 
			
		||||
| 
						 | 
				
			
			@ -244,9 +217,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
					}
 | 
			
		||||
 | 
			
		||||
					expectedColumnCount := 8
 | 
			
		||||
					if tt.isAuthority {
 | 
			
		||||
						expectedColumnCount = 7
 | 
			
		||||
					}
 | 
			
		||||
					if tt.status != "" {
 | 
			
		||||
						expectedColumnCount++
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +226,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
					}
 | 
			
		||||
 | 
			
		||||
					for name, meshed := range tt.expectedRows {
 | 
			
		||||
						if err := validateRowStats(name, meshed, tt.status, rowStats, tt.isAuthority); err != nil {
 | 
			
		||||
						if err := validateRowStats(name, meshed, tt.status, rowStats); err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -271,7 +241,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat, isAuthority bool) error {
 | 
			
		||||
func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat) error {
 | 
			
		||||
	stat, ok := rowStats[name]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("no stats found for [%s]", name)
 | 
			
		||||
| 
						 | 
				
			
			@ -313,12 +283,5 @@ func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats m
 | 
			
		|||
			name, stat.P99Latency)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if stat.TCPOpenConnections != "-" && !isAuthority {
 | 
			
		||||
		_, err := strconv.Atoi(stat.TCPOpenConnections)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("error parsing number of TCP connections [%s]: %w", stat.TCPOpenConnections, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@ import (
 | 
			
		|||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +36,7 @@ func TestMain(m *testing.M) {
 | 
			
		|||
// requesting, and the test will pass.
 | 
			
		||||
func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	var prometheusPod, prometheusAuthority, prometheusNamespace, prometheusDeployment, metricsPod string
 | 
			
		||||
	var prometheusPod, prometheusNamespace, prometheusDeployment, metricsPod string
 | 
			
		||||
	// Get Metrics Pod
 | 
			
		||||
	pods, err := TestHelper.GetPodNamesForDeployment(ctx, TestHelper.GetVizNamespace(), "metrics-api")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,13 +61,11 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
		testutil.Fatalf(t, "expected 1 pod for prometheus, got %d", len(pods))
 | 
			
		||||
	}
 | 
			
		||||
	prometheusPod = pods[0]
 | 
			
		||||
	prometheusAuthority = prometheusDeployment + "." + prometheusNamespace + ".svc.cluster.local:9090"
 | 
			
		||||
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		args         []string
 | 
			
		||||
		expectedRows map[string]string
 | 
			
		||||
		status       string
 | 
			
		||||
		isAuthority  bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetLinkerdNamespace()},
 | 
			
		||||
| 
						 | 
				
			
			@ -103,27 +100,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
				"metrics-api": "1/1",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "po", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("au/%s", prometheusAuthority), "--to-namespace", prometheusNamespace},
 | 
			
		||||
			expectedRows: map[string]string{
 | 
			
		||||
				metricsPod: "1/1",
 | 
			
		||||
			},
 | 
			
		||||
			status: "Running",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
 | 
			
		||||
			expectedRows: map[string]string{
 | 
			
		||||
				prometheusAuthority: "-",
 | 
			
		||||
			},
 | 
			
		||||
			isAuthority: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
 | 
			
		||||
			expectedRows: map[string]string{
 | 
			
		||||
				prometheusAuthority: "-",
 | 
			
		||||
			},
 | 
			
		||||
			isAuthority: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !TestHelper.ExternalPrometheus() {
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +107,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
			args         []string
 | 
			
		||||
			expectedRows map[string]string
 | 
			
		||||
			status       string
 | 
			
		||||
			isAuthority  bool
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +137,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
			args         []string
 | 
			
		||||
			expectedRows map[string]string
 | 
			
		||||
			status       string
 | 
			
		||||
			isAuthority  bool
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +186,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
			args         []string
 | 
			
		||||
			expectedRows map[string]string
 | 
			
		||||
			status       string
 | 
			
		||||
			isAuthority  bool
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				args: []string{"viz", "stat", "svc", "-n", prefixedNs},
 | 
			
		||||
| 
						 | 
				
			
			@ -244,9 +217,6 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
					}
 | 
			
		||||
 | 
			
		||||
					expectedColumnCount := 8
 | 
			
		||||
					if tt.isAuthority {
 | 
			
		||||
						expectedColumnCount = 7
 | 
			
		||||
					}
 | 
			
		||||
					if tt.status != "" {
 | 
			
		||||
						expectedColumnCount++
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +226,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
					}
 | 
			
		||||
 | 
			
		||||
					for name, meshed := range tt.expectedRows {
 | 
			
		||||
						if err := validateRowStats(name, meshed, tt.status, rowStats, tt.isAuthority); err != nil {
 | 
			
		||||
						if err := validateRowStats(name, meshed, tt.status, rowStats); err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -271,7 +241,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 | 
			
		|||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat, isAuthority bool) error {
 | 
			
		||||
func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat) error {
 | 
			
		||||
	stat, ok := rowStats[name]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("No stats found for [%s]", name)
 | 
			
		||||
| 
						 | 
				
			
			@ -313,12 +283,5 @@ func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats m
 | 
			
		|||
			name, stat.P99Latency)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if stat.TCPOpenConnections != "-" && !isAuthority {
 | 
			
		||||
		_, err := strconv.Atoi(stat.TCPOpenConnections)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Error parsing number of TCP connections [%s]: %w", stat.TCPOpenConnections, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ func TestEdges(t *testing.T) {
 | 
			
		|||
	t.Run("Returns an error if request is for authority", func(t *testing.T) {
 | 
			
		||||
		options.outputFormat = tableOutput
 | 
			
		||||
		args := []string{"authority"}
 | 
			
		||||
		expectedError := "Resource type is not supported: authority"
 | 
			
		||||
		expectedError := "cannot find Kubernetes canonical name from friendly name [authority]"
 | 
			
		||||
 | 
			
		||||
		_, err := buildEdgesRequests(args, options)
 | 
			
		||||
		if err == nil || err.Error() != expectedError {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@ import (
 | 
			
		|||
 | 
			
		||||
	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 | 
			
		||||
	"github.com/linkerd/linkerd2/pkg/healthcheck"
 | 
			
		||||
	"github.com/linkerd/linkerd2/pkg/k8s"
 | 
			
		||||
	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"
 | 
			
		||||
| 
						 | 
				
			
			@ -379,7 +378,7 @@ func buildTopRoutesRequest(resource string, options *routesOptions) (*pb.TopRout
 | 
			
		|||
		LabelSelector: options.labelSelector,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options.dstIsService = target.GetType() != k8s.Authority
 | 
			
		||||
	options.dstIsService = true
 | 
			
		||||
 | 
			
		||||
	if options.toResource != "" {
 | 
			
		||||
		if options.toNamespace == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -390,7 +389,7 @@ func buildTopRoutesRequest(resource string, options *routesOptions) (*pb.TopRout
 | 
			
		|||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		options.dstIsService = toRes.GetType() != k8s.Authority
 | 
			
		||||
		options.dstIsService = true
 | 
			
		||||
 | 
			
		||||
		requestParams.ToName = toRes.Name
 | 
			
		||||
		requestParams.ToNamespace = toRes.Namespace
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -372,7 +372,7 @@ func statHasRequestData(stat *pb.BasicStats) bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func isPodOwnerResource(typ string) bool {
 | 
			
		||||
	return typ != k8s.Authority && typ != k8s.Service && typ != k8s.Server && typ != k8s.ServerAuthorization && typ != k8s.AuthorizationPolicy && typ != k8s.HTTPRoute
 | 
			
		||||
	return typ != k8s.Service && typ != k8s.Server && typ != k8s.ServerAuthorization && typ != k8s.AuthorizationPolicy && typ != k8s.HTTPRoute
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeStatsToBuffer(rows []*pb.StatTable_PodGroup_Row, w *tabwriter.Writer, options *statOptions) {
 | 
			
		||||
| 
						 | 
				
			
			@ -433,7 +433,7 @@ func writeStatsToBuffer(rows []*pb.StatTable_PodGroup_Row, w *tabwriter.Writer,
 | 
			
		|||
		statTables[resourceKey][key] = &row{}
 | 
			
		||||
		if resourceKey != k8s.Server && resourceKey != k8s.ServerAuthorization {
 | 
			
		||||
			meshedCount := fmt.Sprintf("%d/%d", r.MeshedPodCount, r.RunningPodCount)
 | 
			
		||||
			if resourceKey == k8s.Authority || resourceKey == k8s.Service {
 | 
			
		||||
			if resourceKey == k8s.Service {
 | 
			
		||||
				meshedCount = "-"
 | 
			
		||||
			}
 | 
			
		||||
			statTables[resourceKey][key] = &row{
 | 
			
		||||
| 
						 | 
				
			
			@ -503,7 +503,7 @@ func showTCPBytes(options *statOptions, resourceType string) bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func showTCPConns(resourceType string) bool {
 | 
			
		||||
	return resourceType != k8s.Authority && resourceType != k8s.ServerAuthorization && resourceType != k8s.AuthorizationPolicy && resourceType != k8s.HTTPRoute
 | 
			
		||||
	return resourceType != k8s.ServerAuthorization && resourceType != k8s.AuthorizationPolicy && resourceType != k8s.HTTPRoute
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType string, w *tabwriter.Writer, maxNameLength, maxNamespaceLength, maxLeafLength, maxApexLength, maxDstLength, maxWeightLength int, options *statOptions) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,9 +125,7 @@ func (s *grpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest
 | 
			
		|||
		statReq.Selector.Resource.Type = resource
 | 
			
		||||
 | 
			
		||||
		go func() {
 | 
			
		||||
			if prometheus.IsNonK8sResourceQuery(statReq.GetSelector().GetResource().GetType()) {
 | 
			
		||||
				resultChan <- s.nonK8sResourceQuery(ctx, statReq)
 | 
			
		||||
			} else if statReq.GetSelector().GetResource().GetType() == k8s.Service {
 | 
			
		||||
			if statReq.GetSelector().GetResource().GetType() == k8s.Service {
 | 
			
		||||
				resultChan <- s.serviceResourceQuery(ctx, statReq)
 | 
			
		||||
			} else if isPolicyResource(statReq.GetSelector().GetResource()) {
 | 
			
		||||
				resultChan <- s.policyResourceQuery(ctx, statReq)
 | 
			
		||||
| 
						 | 
				
			
			@ -362,42 +360,6 @@ func sortTrafficSplitRows(rows []*pb.StatTable_PodGroup_Row) []*pb.StatTable_Pod
 | 
			
		|||
	return rows
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *grpcServer) nonK8sResourceQuery(ctx context.Context, req *pb.StatSummaryRequest) resourceResult {
 | 
			
		||||
	var requestMetrics map[rKey]*pb.BasicStats
 | 
			
		||||
	if !req.SkipStats {
 | 
			
		||||
		var err error
 | 
			
		||||
		requestMetrics, _, err = s.getStatMetrics(ctx, req, req.TimeWindow)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return resourceResult{res: nil, err: err}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	rows := make([]*pb.StatTable_PodGroup_Row, 0)
 | 
			
		||||
 | 
			
		||||
	for rkey, metrics := range requestMetrics {
 | 
			
		||||
		rkey.Type = req.GetSelector().GetResource().GetType()
 | 
			
		||||
 | 
			
		||||
		row := pb.StatTable_PodGroup_Row{
 | 
			
		||||
			Resource: &pb.Resource{
 | 
			
		||||
				Type:      rkey.Type,
 | 
			
		||||
				Namespace: rkey.Namespace,
 | 
			
		||||
				Name:      rkey.Name,
 | 
			
		||||
			},
 | 
			
		||||
			TimeWindow: req.TimeWindow,
 | 
			
		||||
			Stats:      metrics,
 | 
			
		||||
		}
 | 
			
		||||
		rows = append(rows, &row)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rsp := pb.StatTable{
 | 
			
		||||
		Table: &pb.StatTable_PodGroup_{
 | 
			
		||||
			PodGroup: &pb.StatTable_PodGroup{
 | 
			
		||||
				Rows: rows,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return resourceResult{res: &rsp, err: nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// get the list of objects for which we want to return results
 | 
			
		||||
func getResultKeys(
 | 
			
		||||
	req *pb.StatSummaryRequest,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,11 +22,11 @@ type statSumExpected struct {
 | 
			
		|||
 | 
			
		||||
func prometheusMetric(resName string, resType string) model.Vector {
 | 
			
		||||
	return model.Vector{
 | 
			
		||||
		genPromSample(resName, resType, "emojivoto", false),
 | 
			
		||||
		genPromSample(resName, resType, false),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func genPromSample(resName string, resType string, resNs string, isDst bool) *model.Sample {
 | 
			
		||||
func genPromSample(resName string, resType string, isDst bool) *model.Sample {
 | 
			
		||||
	labelName := model.LabelName(resType)
 | 
			
		||||
	namespaceLabel := model.LabelName("namespace")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ func genPromSample(resName string, resType string, resNs string, isDst bool) *mo
 | 
			
		|||
	return &model.Sample{
 | 
			
		||||
		Metric: model.Metric{
 | 
			
		||||
			labelName:        model.LabelValue(resName),
 | 
			
		||||
			namespaceLabel:   model.LabelValue(resNs),
 | 
			
		||||
			namespaceLabel:   model.LabelValue("emojivoto"),
 | 
			
		||||
			"classification": model.LabelValue("success"),
 | 
			
		||||
			"tls":            model.LabelValue("true"),
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -865,7 +865,7 @@ status:
 | 
			
		|||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", "emojivoto", false),
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", false),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{direction="outbound", dst_namespace="emojivoto", dst_pod="emojivoto-2", namespace="emojivoto", pod="emojivoto-1"}[1m])) by (le, namespace, pod))`,
 | 
			
		||||
| 
						 | 
				
			
			@ -922,7 +922,7 @@ status:
 | 
			
		|||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", "emojivoto", false),
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", false),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{direction="outbound", dst_namespace="totallydifferent", dst_pod="emojivoto-2", namespace="emojivoto", pod="emojivoto-1"}[1m])) by (le, namespace, pod))`,
 | 
			
		||||
| 
						 | 
				
			
			@ -990,7 +990,7 @@ status:
 | 
			
		|||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", "emojivoto", true),
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", true),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{direction="outbound", pod="emojivoto-2"}[1m])) by (le, dst_namespace, dst_pod))`,
 | 
			
		||||
| 
						 | 
				
			
			@ -1058,7 +1058,7 @@ status:
 | 
			
		|||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", "emojivoto", true),
 | 
			
		||||
						genPromSample("emojivoto-1", "pod", true),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{direction="outbound", dst_namespace="emojivoto", dst_pod="emojivoto-1", namespace="totallydifferent", pod="emojivoto-2"}[1m])) by (le, dst_namespace, dst_pod))`,
 | 
			
		||||
| 
						 | 
				
			
			@ -1226,28 +1226,6 @@ status:
 | 
			
		|||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									Table: &pb.StatTable_PodGroup_{
 | 
			
		||||
										PodGroup: &pb.StatTable_PodGroup{
 | 
			
		||||
											Rows: []*pb.StatTable_PodGroup_Row{
 | 
			
		||||
												{
 | 
			
		||||
													Resource: &pb.Resource{
 | 
			
		||||
														Namespace: "emojivoto",
 | 
			
		||||
														Type:      pkgK8s.Authority,
 | 
			
		||||
													},
 | 
			
		||||
													TimeWindow: "1m",
 | 
			
		||||
													Stats: &pb.BasicStats{
 | 
			
		||||
														SuccessCount: 123,
 | 
			
		||||
														FailureCount: 0,
 | 
			
		||||
														LatencyMsP50: 123,
 | 
			
		||||
														LatencyMsP95: 123,
 | 
			
		||||
														LatencyMsP99: 123,
 | 
			
		||||
													},
 | 
			
		||||
												},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									Table: &pb.StatTable_PodGroup_{
 | 
			
		||||
										PodGroup: &pb.StatTable_PodGroup{
 | 
			
		||||
| 
						 | 
				
			
			@ -1686,146 +1664,6 @@ status:
 | 
			
		|||
		})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Queries prometheus for authority stats", func(t *testing.T) {
 | 
			
		||||
		expectations := []statSumExpected{
 | 
			
		||||
			{
 | 
			
		||||
				expectedStatRPC: expectedStatRPC{
 | 
			
		||||
					err: nil,
 | 
			
		||||
					k8sConfigs: []string{`
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: Pod
 | 
			
		||||
metadata:
 | 
			
		||||
  name: emojivoto-1
 | 
			
		||||
  namespace: emojivoto
 | 
			
		||||
  labels:
 | 
			
		||||
    app: emoji-svc
 | 
			
		||||
    linkerd.io/control-plane-ns: linkerd
 | 
			
		||||
status:
 | 
			
		||||
  phase: Running
 | 
			
		||||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("10.1.1.239:9995", "authority", "linkerd", false),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{direction="inbound", namespace="linkerd"}[1m])) by (le, namespace, authority))`,
 | 
			
		||||
						`histogram_quantile(0.95, sum(irate(response_latency_ms_bucket{direction="inbound", namespace="linkerd"}[1m])) by (le, namespace, authority))`,
 | 
			
		||||
						`histogram_quantile(0.99, sum(irate(response_latency_ms_bucket{direction="inbound", namespace="linkerd"}[1m])) by (le, namespace, authority))`,
 | 
			
		||||
						`sum(increase(response_total{direction="inbound", namespace="linkerd"}[1m])) by (namespace, authority, classification, tls)`,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: &pb.StatSummaryRequest{
 | 
			
		||||
					Selector: &pb.ResourceSelection{
 | 
			
		||||
						Resource: &pb.Resource{
 | 
			
		||||
							Namespace: "linkerd",
 | 
			
		||||
							Type:      pkgK8s.Authority,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					TimeWindow: "1m",
 | 
			
		||||
				},
 | 
			
		||||
				expectedResponse: GenStatSummaryResponse("10.1.1.239:9995", pkgK8s.Authority, []string{"linkerd"}, nil, true, false),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		testStatSummary(t, expectations)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Queries prometheus for authority stats when --from deployment is used", func(t *testing.T) {
 | 
			
		||||
		expectations := []statSumExpected{
 | 
			
		||||
			{
 | 
			
		||||
				expectedStatRPC: expectedStatRPC{
 | 
			
		||||
					err: nil,
 | 
			
		||||
					k8sConfigs: []string{`
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: Pod
 | 
			
		||||
metadata:
 | 
			
		||||
  name: emojivoto-1
 | 
			
		||||
  namespace: emojivoto
 | 
			
		||||
  labels:
 | 
			
		||||
    app: emoji-svc
 | 
			
		||||
    linkerd.io/control-plane-ns: linkerd
 | 
			
		||||
status:
 | 
			
		||||
  phase: Running
 | 
			
		||||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("10.1.1.239:9995", "authority", "linkerd", false),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{deployment="emojivoto", direction="outbound"}[1m])) by (le, dst_namespace, authority))`,
 | 
			
		||||
						`histogram_quantile(0.95, sum(irate(response_latency_ms_bucket{deployment="emojivoto", direction="outbound"}[1m])) by (le, dst_namespace, authority))`,
 | 
			
		||||
						`histogram_quantile(0.99, sum(irate(response_latency_ms_bucket{deployment="emojivoto", direction="outbound"}[1m])) by (le, dst_namespace, authority))`,
 | 
			
		||||
						`sum(increase(response_total{deployment="emojivoto", direction="outbound"}[1m])) by (dst_namespace, authority, classification, tls)`,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: &pb.StatSummaryRequest{
 | 
			
		||||
					Selector: &pb.ResourceSelection{
 | 
			
		||||
						Resource: &pb.Resource{
 | 
			
		||||
							Namespace: "linkerd",
 | 
			
		||||
							Type:      pkgK8s.Authority,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					TimeWindow: "1m",
 | 
			
		||||
					Outbound: &pb.StatSummaryRequest_FromResource{
 | 
			
		||||
						FromResource: &pb.Resource{
 | 
			
		||||
							Name:      "emojivoto",
 | 
			
		||||
							Namespace: "",
 | 
			
		||||
							Type:      pkgK8s.Deployment,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				expectedResponse: GenStatSummaryResponse("10.1.1.239:9995", pkgK8s.Authority, []string{""}, nil, true, false),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		testStatSummary(t, expectations)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Queries prometheus for a named authority", func(t *testing.T) {
 | 
			
		||||
		expectations := []statSumExpected{
 | 
			
		||||
			{
 | 
			
		||||
				expectedStatRPC: expectedStatRPC{
 | 
			
		||||
					err: nil,
 | 
			
		||||
					k8sConfigs: []string{`
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: Pod
 | 
			
		||||
metadata:
 | 
			
		||||
  name: emojivoto-1
 | 
			
		||||
  namespace: emojivoto
 | 
			
		||||
  labels:
 | 
			
		||||
    app: emoji-svc
 | 
			
		||||
    linkerd.io/control-plane-ns: linkerd
 | 
			
		||||
status:
 | 
			
		||||
  phase: Running
 | 
			
		||||
`,
 | 
			
		||||
					},
 | 
			
		||||
					mockPromResponse: model.Vector{
 | 
			
		||||
						genPromSample("10.1.1.239:9995", "authority", "linkerd", false),
 | 
			
		||||
					},
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(response_latency_ms_bucket{authority="10.1.1.239:9995", direction="inbound", namespace="linkerd"}[1m])) by (le, namespace, authority))`,
 | 
			
		||||
						`histogram_quantile(0.95, sum(irate(response_latency_ms_bucket{authority="10.1.1.239:9995", direction="inbound", namespace="linkerd"}[1m])) by (le, namespace, authority))`,
 | 
			
		||||
						`histogram_quantile(0.99, sum(irate(response_latency_ms_bucket{authority="10.1.1.239:9995", direction="inbound", namespace="linkerd"}[1m])) by (le, namespace, authority))`,
 | 
			
		||||
						`sum(increase(response_total{authority="10.1.1.239:9995", direction="inbound", namespace="linkerd"}[1m])) by (namespace, authority, classification, tls)`,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: &pb.StatSummaryRequest{
 | 
			
		||||
					Selector: &pb.ResourceSelection{
 | 
			
		||||
						Resource: &pb.Resource{
 | 
			
		||||
							Namespace: "linkerd",
 | 
			
		||||
							Type:      pkgK8s.Authority,
 | 
			
		||||
							Name:      "10.1.1.239:9995",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					TimeWindow: "1m",
 | 
			
		||||
				},
 | 
			
		||||
				expectedResponse: GenStatSummaryResponse("10.1.1.239:9995", pkgK8s.Authority, []string{"linkerd"}, nil, true, false),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		testStatSummary(t, expectations)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Stats returned are nil when SkipStats is true", func(t *testing.T) {
 | 
			
		||||
		expectations := []statSumExpected{
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ package api
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
| 
						 | 
				
			
			@ -58,11 +57,6 @@ func (s *grpcServer) TopRoutes(ctx context.Context, req *pb.TopRoutesRequest) (*
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if targetResource.GetType() == k8s.Authority {
 | 
			
		||||
		// Authority cannot be the target because authorities don't have namespaces,
 | 
			
		||||
		// therefore there is no namespace in which to look for a service profile.
 | 
			
		||||
		return topRoutesError(req, "Authority cannot be the target of a routes query; try using an authority in the --to flag instead"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = s.validateTimeWindow(ctx, req.TimeWindow)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -139,31 +133,22 @@ func (s *grpcServer) topRoutesFor(ctx context.Context, req *pb.TopRoutesRequest,
 | 
			
		|||
 | 
			
		||||
	profiles := make(map[string]*sp.ServiceProfile)
 | 
			
		||||
 | 
			
		||||
	if requestedResource.GetType() == k8s.Authority {
 | 
			
		||||
		// Authorities may not be a source, so we know this is a ToResource.
 | 
			
		||||
		profiles, err = s.getProfilesForAuthority(requestedResource.GetName(), clientNs, labelSelector)
 | 
			
		||||
	// Lookup individual resource objects.
 | 
			
		||||
	objects, err := s.k8sAPI.GetObjects(requestedResource.Namespace, requestedResource.Type, requestedResource.Name, labelSelector)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// Find service profiles for all services in all objects in the resource.
 | 
			
		||||
	for _, obj := range objects {
 | 
			
		||||
		// Lookup services for each object.
 | 
			
		||||
		services, err := s.k8sAPI.GetServicesFor(obj, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// Non-authority resource.
 | 
			
		||||
		// Lookup individual resource objects.
 | 
			
		||||
		objects, err := s.k8sAPI.GetObjects(requestedResource.Namespace, requestedResource.Type, requestedResource.Name, labelSelector)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		// Find service profiles for all services in all objects in the resource.
 | 
			
		||||
		for _, obj := range objects {
 | 
			
		||||
			// Lookup services for each object.
 | 
			
		||||
			services, err := s.k8sAPI.GetServicesFor(obj, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, svc := range services {
 | 
			
		||||
				p := s.k8sAPI.GetServiceProfileFor(svc, clientNs, s.clusterDomain)
 | 
			
		||||
				profiles[svc.GetName()] = p
 | 
			
		||||
			}
 | 
			
		||||
		for _, svc := range services {
 | 
			
		||||
			p := s.k8sAPI.GetServiceProfileFor(svc, clientNs, s.clusterDomain)
 | 
			
		||||
			profiles[svc.GetName()] = p
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -197,43 +182,13 @@ func validateRequest(req *pb.TopRoutesRequest) *pb.TopRoutesResponse {
 | 
			
		|||
	if req.GetNone() == nil {
 | 
			
		||||
		// This is an outbound (--to) request.
 | 
			
		||||
		targetType := req.GetSelector().GetResource().GetType()
 | 
			
		||||
		if targetType == k8s.Service || targetType == k8s.Authority {
 | 
			
		||||
		if targetType == k8s.Service {
 | 
			
		||||
			return topRoutesError(req, fmt.Sprintf("The %s resource type is not supported with 'to' queries", targetType))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *grpcServer) getProfilesForAuthority(authority string, clientNs string, labelSelector labels.Selector) (map[string]*sp.ServiceProfile, error) {
 | 
			
		||||
	if authority == "" {
 | 
			
		||||
		// All authorities
 | 
			
		||||
		ps, err := s.k8sAPI.SP().Lister().ServiceProfiles(clientNs).List(labelSelector)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(ps) == 0 {
 | 
			
		||||
			return nil, errors.New("No ServiceProfiles found")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		profiles := make(map[string]*sp.ServiceProfile)
 | 
			
		||||
 | 
			
		||||
		for _, p := range ps {
 | 
			
		||||
			profiles[p.Name] = p
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return profiles, nil
 | 
			
		||||
	}
 | 
			
		||||
	// Specific authority
 | 
			
		||||
	p, err := s.k8sAPI.SP().Lister().ServiceProfiles(clientNs).Get(authority)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return map[string]*sp.ServiceProfile{
 | 
			
		||||
		p.Name: p,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *grpcServer) getRouteMetrics(ctx context.Context, req *pb.TopRoutesRequest, profiles map[string]*sp.ServiceProfile, resource *pb.Resource) (indexedTable, error) {
 | 
			
		||||
	timeWindow := req.TimeWindow
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -460,44 +460,4 @@ func TestTopRoutes(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
		testTopRoutes(t, expectations)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Successfully performs an outbound authority query", func(t *testing.T) {
 | 
			
		||||
		routes := []string{"/a"}
 | 
			
		||||
		counts := []uint64{123}
 | 
			
		||||
		expectations := []topRoutesExpected{
 | 
			
		||||
			{
 | 
			
		||||
				expectedStatRPC: expectedStatRPC{
 | 
			
		||||
					err:              nil,
 | 
			
		||||
					mockPromResponse: routesMetric([]string{"/a"}),
 | 
			
		||||
					expectedPrometheusQueries: []string{
 | 
			
		||||
						`histogram_quantile(0.5, sum(irate(route_response_latency_ms_bucket{deployment="books", direction="outbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (le, dst, rt_route))`,
 | 
			
		||||
						`histogram_quantile(0.95, sum(irate(route_response_latency_ms_bucket{deployment="books", direction="outbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (le, dst, rt_route))`,
 | 
			
		||||
						`histogram_quantile(0.99, sum(irate(route_response_latency_ms_bucket{deployment="books", direction="outbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (le, dst, rt_route))`,
 | 
			
		||||
						`sum(increase(route_response_total{deployment="books", direction="outbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (rt_route, dst, classification)`,
 | 
			
		||||
						`sum(increase(route_actual_response_total{deployment="books", direction="outbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (rt_route, dst, classification)`,
 | 
			
		||||
					},
 | 
			
		||||
					k8sConfigs: booksConfig,
 | 
			
		||||
				},
 | 
			
		||||
				req: &pb.TopRoutesRequest{
 | 
			
		||||
					Selector: &pb.ResourceSelection{
 | 
			
		||||
						Resource: &pb.Resource{
 | 
			
		||||
							Namespace: "default",
 | 
			
		||||
							Type:      pkgK8s.Deployment,
 | 
			
		||||
							Name:      "books",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Outbound: &pb.TopRoutesRequest_ToResource{
 | 
			
		||||
						ToResource: &pb.Resource{
 | 
			
		||||
							Type: pkgK8s.Authority,
 | 
			
		||||
							Name: "books.default.svc.cluster.local",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					TimeWindow: "1m",
 | 
			
		||||
				},
 | 
			
		||||
				expectedResponse: GenTopRoutesResponse(routes, counts, true, "books.default.svc.cluster.local"),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		testTopRoutes(t, expectations)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -274,9 +274,7 @@ func validateFromResourceType(resourceType string) (string, error) {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if name == k8s.Authority {
 | 
			
		||||
		return "", errors.New("cannot query traffic --from an authority")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,10 +63,7 @@ func QueryLabels(resource *pb.Resource) model.LabelSet {
 | 
			
		|||
// note that metricToKey assumes the label ordering (namespace, name)
 | 
			
		||||
func DstGroupByLabelNames(resource *pb.Resource) model.LabelNames {
 | 
			
		||||
	names := model.LabelNames{DstNamespaceLabel}
 | 
			
		||||
 | 
			
		||||
	if IsNonK8sResourceQuery(resource.GetType()) {
 | 
			
		||||
		names = append(names, ResourceType(resource))
 | 
			
		||||
	} else if resource.Type != k8s.Namespace {
 | 
			
		||||
	if resource.Type != k8s.Namespace {
 | 
			
		||||
		names = append(names, "dst_"+ResourceType(resource))
 | 
			
		||||
	}
 | 
			
		||||
	return names
 | 
			
		||||
| 
						 | 
				
			
			@ -76,14 +73,11 @@ func DstGroupByLabelNames(resource *pb.Resource) model.LabelNames {
 | 
			
		|||
func DstQueryLabels(resource *pb.Resource) model.LabelSet {
 | 
			
		||||
	set := model.LabelSet{}
 | 
			
		||||
	if resource.Name != "" {
 | 
			
		||||
		if IsNonK8sResourceQuery(resource.GetType()) {
 | 
			
		||||
			set[ResourceType(resource)] = model.LabelValue(resource.Name)
 | 
			
		||||
		} else {
 | 
			
		||||
			set["dst_"+ResourceType(resource)] = model.LabelValue(resource.Name)
 | 
			
		||||
			if shouldAddNamespaceLabel(resource) {
 | 
			
		||||
				set[DstNamespaceLabel] = model.LabelValue(resource.Namespace)
 | 
			
		||||
			}
 | 
			
		||||
		set["dst_"+ResourceType(resource)] = model.LabelValue(resource.Name)
 | 
			
		||||
		if shouldAddNamespaceLabel(resource) {
 | 
			
		||||
			set[DstNamespaceLabel] = model.LabelValue(resource.Namespace)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return set
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +92,3 @@ func ResourceType(resource *pb.Resource) model.LabelName {
 | 
			
		|||
func shouldAddNamespaceLabel(resource *pb.Resource) bool {
 | 
			
		||||
	return resource.Type != k8s.Namespace && resource.Namespace != ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsNonK8sResourceQuery(resourceType string) bool {
 | 
			
		||||
	return resourceType == k8s.Authority
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,6 @@ import (
 | 
			
		|||
// - target resource on an outbound 'to' query
 | 
			
		||||
// - destination resource on an outbound 'from' query
 | 
			
		||||
var ValidTargets = []string{
 | 
			
		||||
	k8s.Authority,
 | 
			
		||||
	k8s.CronJob,
 | 
			
		||||
	k8s.DaemonSet,
 | 
			
		||||
	k8s.Deployment,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue