diff --git a/cli/cmd/stat_summary.go b/cli/cmd/stat.go similarity index 70% rename from cli/cmd/stat_summary.go rename to cli/cmd/stat.go index 95d1c9c1e..412bdb86e 100644 --- a/cli/cmd/stat_summary.go +++ b/cli/cmd/stat.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "os" "sort" "strings" "text/tabwriter" @@ -13,13 +14,14 @@ import ( "github.com/prometheus/common/log" "github.com/runconduit/conduit/controller/api/util" pb "github.com/runconduit/conduit/controller/gen/public" + "github.com/runconduit/conduit/pkg/k8s" "github.com/spf13/cobra" "k8s.io/api/core/v1" ) var timeWindow, namespace, resourceType, resourceName string -var outToNamespace, outToType, outToName string -var outFromNamespace, outFromType, outFromName string +var toNamespace, toType, toName string +var fromNamespace, fromType, fromName string var allNamespaces bool var statCmd = &cobra.Command{ @@ -29,7 +31,8 @@ var statCmd = &cobra.Command{ Valid resource types include: - * deployment + * deployments + * namespaces This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases. If no resource name is specified, displays stats about all resources of the specified RESOURCETYPE`, @@ -37,9 +40,15 @@ If no resource name is specified, displays stats about all resources of the spec conduit stat deployments -n test # Get the hello1 deployment in the test namespace. - conduit stat deployments hello1 -n test`, + conduit stat deployments hello1 -n test + + # Get the test namespace. + conduit stat namespaces test + + # Get all namespaces. + conduit stat --all-namespaces=true namespaces`, Args: cobra.RangeArgs(1, 2), - ValidArgs: []string{"deployment"}, + ValidArgs: []string{k8s.KubernetesDeployments, k8s.KubernetesNamespaces}, RunE: func(cmd *cobra.Command, args []string) error { switch len(args) { case 1: @@ -71,12 +80,12 @@ func init() { RootCmd.AddCommand(statCmd) statCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "default", "Namespace of the specified resource") statCmd.PersistentFlags().StringVarP(&timeWindow, "time-window", "t", "1m", "Stat window (one of: \"10s\", \"1m\", \"10m\", \"1h\")") - statCmd.PersistentFlags().StringVar(&outToName, "out-to", "", "If present, restricts outbound stats to the specified resource name") - statCmd.PersistentFlags().StringVar(&outToNamespace, "out-to-namespace", "", "Sets the namespace used to lookup the \"--out-to\" resource; by default the current \"--namespace\" is used") - statCmd.PersistentFlags().StringVar(&outToType, "out-to-resource", "", "If present, restricts outbound stats to the specified resource type") - statCmd.PersistentFlags().StringVar(&outFromName, "out-from", "", "If present, restricts outbound stats to the specified resource name") - statCmd.PersistentFlags().StringVar(&outFromNamespace, "out-from-namespace", "", "Sets the namespace used to lookup the \"--out-from\" resource; by default the current \"--namespace\" is used") - statCmd.PersistentFlags().StringVar(&outFromType, "out-from-resource", "", "If present, restricts outbound stats to the specified resource type") + statCmd.PersistentFlags().StringVar(&toName, "to", "", "If present, restricts outbound stats to the specified resource name") + statCmd.PersistentFlags().StringVar(&toNamespace, "to-namespace", "", "Sets the namespace used to lookup the \"--to\" resource; by default the current \"--namespace\" is used") + statCmd.PersistentFlags().StringVar(&toType, "to-resource", "", "Sets the resource type used to lookup the \"--to\" resource; by default the RESOURCETYPE is used") + statCmd.PersistentFlags().StringVar(&fromName, "from", "", "If present, restricts outbound stats from the specified resource name") + statCmd.PersistentFlags().StringVar(&fromNamespace, "from-namespace", "", "Sets the namespace used from lookup the \"--from\" resource; by default the current \"--namespace\" is used") + statCmd.PersistentFlags().StringVar(&fromType, "from-resource", "", "Sets the resource type used to lookup the \"--from\" resource; by default the RESOURCETYPE is used") statCmd.PersistentFlags().BoolVar(&allNamespaces, "all-namespaces", false, "If present, returns stats across all namespaces, ignoring the \"--namespace\" flag") } @@ -110,8 +119,7 @@ func renderStats(resp *pb.StatSummaryResponse) string { const padding = 3 -type row struct { - meshed string +type rowStats struct { requestRate float64 successRate float64 latencyP50 uint64 @@ -119,6 +127,11 @@ type row struct { latencyP99 uint64 } +type row struct { + meshed string + *rowStats +} + func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) { nameHeader := "NAME" maxNameLength := len(nameHeader) @@ -147,15 +160,22 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) { } if r.Stats != nil { - stats[key].requestRate = getRequestRate(*r) - stats[key].successRate = getSuccessRate(*r) - stats[key].latencyP50 = r.Stats.LatencyMsP50 - stats[key].latencyP95 = r.Stats.LatencyMsP95 - stats[key].latencyP99 = r.Stats.LatencyMsP99 + stats[key].rowStats = &rowStats{ + requestRate: getRequestRate(*r), + successRate: getSuccessRate(*r), + latencyP50: r.Stats.LatencyMsP50, + latencyP95: r.Stats.LatencyMsP95, + latencyP99: r.Stats.LatencyMsP99, + } } } } + if len(stats) == 0 { + fmt.Fprintln(os.Stderr, "No traffic found.") + os.Exit(0) + } + headers := make([]string, 0) if allNamespaces { headers = append(headers, @@ -170,6 +190,7 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) { "LATENCY_P95", "LATENCY_P99\t", // trailing \t is required to format last column }...) + fmt.Fprintln(w, strings.Join(headers, "\t")) sortedKeys := sortStatsKeys(stats) @@ -179,23 +200,32 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) { name := parts[1] values := make([]interface{}, 0) templateString := "%s\t%s\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t\n" + templateStringEmpty := "%s\t%s\t-\t-\t-\t-\t-\t\n" if allNamespaces { values = append(values, namespace+strings.Repeat(" ", maxNamespaceLength-len(namespace))) templateString = "%s\t" + templateString + templateStringEmpty = "%s\t" + templateStringEmpty } values = append(values, []interface{}{ name + strings.Repeat(" ", maxNameLength-len(name)), stats[key].meshed, - stats[key].successRate * 100, - stats[key].requestRate, - stats[key].latencyP50, - stats[key].latencyP95, - stats[key].latencyP99, }...) - fmt.Fprintf(w, templateString, values...) + if stats[key].rowStats != nil { + values = append(values, []interface{}{ + stats[key].successRate * 100, + stats[key].requestRate, + stats[key].latencyP50, + stats[key].latencyP95, + stats[key].latencyP99, + }...) + + fmt.Fprintf(w, templateString, values...) + } else { + fmt.Fprintf(w, templateStringEmpty, values...) + } } } @@ -208,16 +238,16 @@ func buildStatSummaryRequest() (*pb.StatSummaryRequest, error) { } requestParams := util.StatSummaryRequestParams{ - TimeWindow: timeWindow, - ResourceName: resourceName, - ResourceType: resourceType, - Namespace: targetNamespace, - OutToName: outToName, - OutToType: outToType, - OutToNamespace: outToNamespace, - OutFromName: outFromName, - OutFromType: outFromType, - OutFromNamespace: outFromNamespace, + TimeWindow: timeWindow, + ResourceName: resourceName, + ResourceType: resourceType, + Namespace: targetNamespace, + ToName: toName, + ToType: toType, + ToNamespace: toNamespace, + FromName: fromName, + FromType: fromType, + FromNamespace: fromNamespace, } return util.BuildStatSummaryRequest(requestParams) diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden index b2ce28e74..e49e46953 100755 --- a/cli/cmd/testdata/install_default.golden +++ b/cli/cmd/testdata/install_default.golden @@ -23,7 +23,7 @@ rules: resources: ["deployments", "replicasets"] verbs: ["list", "get", "watch"] - apiGroups: [""] - resources: ["pods", "endpoints", "services"] + resources: ["pods", "endpoints", "services", "namespaces"] verbs: ["list", "get", "watch"] --- diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden index 8075412c0..d6d31c363 100644 --- a/cli/cmd/testdata/install_output.golden +++ b/cli/cmd/testdata/install_output.golden @@ -23,7 +23,7 @@ rules: resources: ["deployments", "replicasets"] verbs: ["list", "get", "watch"] - apiGroups: [""] - resources: ["pods", "endpoints", "services"] + resources: ["pods", "endpoints", "services", "namespaces"] verbs: ["list", "get", "watch"] --- diff --git a/cli/install/template.go b/cli/install/template.go index 4f3b3d056..903a34203 100644 --- a/cli/install/template.go +++ b/cli/install/template.go @@ -26,7 +26,7 @@ rules: resources: ["deployments", "replicasets"] verbs: ["list", "get", "watch"] - apiGroups: [""] - resources: ["pods", "endpoints", "services"] + resources: ["pods", "endpoints", "services", "namespaces"] verbs: ["list", "get", "watch"] --- diff --git a/controller/api/public/client.go b/controller/api/public/client.go index b9b58a0c3..25974f0b3 100644 --- a/controller/api/public/client.go +++ b/controller/api/public/client.go @@ -79,7 +79,7 @@ func (c *grpcOverHttpClient) Tap(ctx context.Context, req *pb.TapRequest, _ ...g 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]", url.String()) + 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 diff --git a/controller/api/public/grpc_server.go b/controller/api/public/grpc_server.go index a7e647c5c..b8f429cd8 100644 --- a/controller/api/public/grpc_server.go +++ b/controller/api/public/grpc_server.go @@ -24,6 +24,7 @@ type ( grpcServer struct { prometheusAPI promv1.API tapClient tapPb.TapClient + namespaceLister corelisters.NamespaceLister deployLister applisters.DeploymentLister replicaSetLister applisters.ReplicaSetLister podLister corelisters.PodLister @@ -43,6 +44,7 @@ const ( func newGrpcServer( promAPI promv1.API, tapClient tapPb.TapClient, + namespaceLister corelisters.NamespaceLister, deployLister applisters.DeploymentLister, replicaSetLister applisters.ReplicaSetLister, podLister corelisters.PodLister, @@ -52,6 +54,7 @@ func newGrpcServer( return &grpcServer{ prometheusAPI: promAPI, tapClient: tapClient, + namespaceLister: namespaceLister, deployLister: deployLister, replicaSetLister: replicaSetLister, podLister: podLister, diff --git a/controller/api/public/grpc_server_test.go b/controller/api/public/grpc_server_test.go index 4823e3550..50a3a6b01 100644 --- a/controller/api/public/grpc_server_test.go +++ b/controller/api/public/grpc_server_test.go @@ -166,6 +166,7 @@ spec: clientSet := fake.NewSimpleClientset(k8sObjs...) sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute) + namespaceInformer := sharedInformers.Core().V1().Namespaces() deployInformer := sharedInformers.Apps().V1beta2().Deployments() replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets() podInformer := sharedInformers.Core().V1().Pods() @@ -173,6 +174,7 @@ spec: fakeGrpcServer := newGrpcServer( &MockProm{Res: exp.promRes}, tap.NewTapClient(nil), + namespaceInformer.Lister(), deployInformer.Lister(), replicaSetInformer.Lister(), podInformer.Lister(), @@ -183,6 +185,7 @@ spec: sharedInformers.Start(stopCh) if !cache.WaitForCacheSync( stopCh, + namespaceInformer.Informer().HasSynced, deployInformer.Informer().HasSynced, replicaSetInformer.Informer().HasSynced, podInformer.Informer().HasSynced, diff --git a/controller/api/public/http_server.go b/controller/api/public/http_server.go index 5a0494497..8bdc2a8b3 100644 --- a/controller/api/public/http_server.go +++ b/controller/api/public/http_server.go @@ -199,6 +199,7 @@ func NewServer( addr string, prometheusClient promApi.Client, tapClient tapPb.TapClient, + namespaceLister corelisters.NamespaceLister, deployLister applisters.DeploymentLister, replicaSetLister applisters.ReplicaSetLister, podLister corelisters.PodLister, @@ -209,6 +210,7 @@ func NewServer( grpcServer: newGrpcServer( promv1.NewAPI(prometheusClient), tapClient, + namespaceLister, deployLister, replicaSetLister, podLister, diff --git a/controller/api/public/stat_summary.go b/controller/api/public/stat_summary.go index e7c7b3f74..e7bdf45cd 100644 --- a/controller/api/public/stat_summary.go +++ b/controller/api/public/stat_summary.go @@ -2,7 +2,6 @@ package public import ( "context" - "errors" "fmt" "math" "strings" @@ -15,6 +14,7 @@ import ( log "github.com/sirupsen/logrus" appsv1beta2 "k8s.io/api/apps/v1beta2" apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" @@ -28,22 +28,24 @@ type promResult struct { } const ( - reqQuery = "sum(increase(response_total{%s}[%s])) by (%s, namespace, classification)" - latencyQuantileQuery = "histogram_quantile(%s, sum(irate(response_latency_ms_bucket{%s}[%s])) by (le, namespace, %s))" + reqQuery = "sum(increase(response_total%s[%s])) by (%s, classification)" + latencyQuantileQuery = "histogram_quantile(%s, sum(irate(response_latency_ms_bucket%s[%s])) by (le, %s))" promRequests = promType("QUERY_REQUESTS") promLatencyP50 = promType("0.5") promLatencyP95 = promType("0.95") promLatencyP99 = promType("0.99") - namespaceLabel = "namespace" + namespaceLabel = model.LabelName("namespace") + dstNamespaceLabel = model.LabelName("dst_namespace") ) var ( promTypes = []promType{promRequests, promLatencyP50, promLatencyP95, promLatencyP99} - k8sResourceTypesToPromLabels = map[string]string{ + k8sResourceTypesToPromLabels = map[string]model.LabelName{ k8s.KubernetesDeployments: "deployment", + k8s.KubernetesNamespaces: namespaceLabel, } ) @@ -53,15 +55,31 @@ type meshedCount struct { } func (s *grpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest) (*pb.StatSummaryResponse, error) { + var err error + var objectMap map[string]metav1.ObjectMeta + var meshCount map[string]*meshedCount + switch req.Selector.Resource.Type { case k8s.KubernetesDeployments: - return s.deploymentQuery(ctx, req) + objectMap, meshCount, err = s.getDeployments(req.Selector.Resource) + case k8s.KubernetesNamespaces: + objectMap, meshCount, err = s.getNamespaces(req.Selector.Resource) default: - return nil, errors.New("Unimplemented resource type: " + req.Selector.Resource.Type) + err = fmt.Errorf("Unimplemented resource type: %v", req.Selector.Resource.Type) } + if err != nil { + return nil, err + } + + return s.objectQuery(ctx, req, objectMap, meshCount) } -func (s *grpcServer) deploymentQuery(ctx context.Context, req *pb.StatSummaryRequest) (*pb.StatSummaryResponse, error) { +func (s *grpcServer) objectQuery( + ctx context.Context, + req *pb.StatSummaryRequest, + objects map[string]metav1.ObjectMeta, + meshCount map[string]*meshedCount, +) (*pb.StatSummaryResponse, error) { rows := make([]*pb.StatTable_PodGroup_Row, 0) timeWindow, err := apiUtil.GetWindowString(req.TimeWindow) @@ -69,23 +87,29 @@ func (s *grpcServer) deploymentQuery(ctx context.Context, req *pb.StatSummaryReq return nil, err } - deployments, meshCount, err := s.getDeployments(req.Selector.Resource) + requestMetrics, err := s.getRequests(ctx, req, timeWindow) if err != nil { return nil, err } - requestLabels := buildRequestLabels(req) - groupBy := promResourceType(req.Selector.Resource) + var keys []string - requestMetrics, err := s.getRequests(ctx, requestLabels, groupBy, timeWindow) - if err != nil { - return nil, err + if req.GetOutbound() == nil || req.GetNone() != nil { + // if this request doesn't have outbound filtering, return all rows + for key := range objects { + keys = append(keys, key) + } + } else { + // otherwise only return rows for which we have stats + for key := range requestMetrics { + keys = append(keys, key) + } } - for _, resource := range deployments { - key, err := cache.MetaNamespaceKeyFunc(resource) - if err != nil { - return nil, err + for _, key := range keys { + resource, ok := objects[key] + if !ok { + continue } row := pb.StatTable_PodGroup_Row{ @@ -125,74 +149,82 @@ func (s *grpcServer) deploymentQuery(ctx context.Context, req *pb.StatSummaryReq return &rsp, nil } -func promLabel(key, val string) string { - return fmt.Sprintf("%s=\"%s\"", key, val) +func promLabelNames(resource *pb.Resource) model.LabelNames { + names := model.LabelNames{namespaceLabel} + if resource.Type != k8s.KubernetesNamespaces { + names = append(names, promResourceType(resource)) + } + return names } -func promNameLabel(resource *pb.Resource) string { - return promLabel(promResourceType(resource), resource.Name) +func promDstLabelNames(resource *pb.Resource) model.LabelNames { + names := model.LabelNames{dstNamespaceLabel} + if resource.Type != k8s.KubernetesNamespaces { + names = append(names, "dst_"+promResourceType(resource)) + } + return names } -func promNamespaceLabel(resource *pb.Resource) string { - return promLabel(namespaceLabel, resource.Namespace) +func promLabels(resource *pb.Resource) model.LabelSet { + set := model.LabelSet{} + if resource.Name != "" { + set[promResourceType(resource)] = model.LabelValue(resource.Name) + } + if resource.Type != k8s.KubernetesNamespaces && resource.Namespace != "" { + set[namespaceLabel] = model.LabelValue(resource.Namespace) + } + return set } -func promDstLabels(resource *pb.Resource) []string { - return []string{ - promLabel("dst_"+namespaceLabel, resource.Namespace), - promLabel("dst_"+promResourceType(resource), resource.Name), +func promDstLabels(resource *pb.Resource) model.LabelSet { + set := model.LabelSet{} + if resource.Name != "" { + set["dst_"+promResourceType(resource)] = model.LabelValue(resource.Name) + } + if resource.Type != k8s.KubernetesNamespaces && resource.Namespace != "" { + set[dstNamespaceLabel] = model.LabelValue(resource.Namespace) + } + return set +} + +func promDirectionLabels(direction string) model.LabelSet { + return model.LabelSet{ + model.LabelName("direction"): model.LabelValue(direction), } } -func promResourceType(resource *pb.Resource) string { +func promResourceType(resource *pb.Resource) model.LabelName { return k8sResourceTypesToPromLabels[resource.Type] } -func buildRequestLabels(req *pb.StatSummaryRequest) string { - labels := []string{} +func buildRequestLabels(req *pb.StatSummaryRequest) (model.LabelSet, model.LabelNames) { + labels := model.LabelSet{} + aggregations := model.LabelNames{} - var direction string - switch req.Outbound.(type) { - case *pb.StatSummaryRequest_OutToResource: - direction = "outbound" - labels = append(labels, promDstLabels(req.GetOutToResource())...) + switch out := req.Outbound.(type) { + case *pb.StatSummaryRequest_ToResource: + aggregations = promLabelNames(req.Selector.Resource) + labels = labels.Merge(promDstLabels(out.ToResource)) + labels = labels.Merge(promLabels(req.Selector.Resource)) + labels = labels.Merge(promDirectionLabels("outbound")) - case *pb.StatSummaryRequest_OutFromResource: - direction = "outbound" - labels = append(labels, promDstLabels(req.Selector.Resource)...) - - outFromNs := req.GetOutFromResource().Namespace - if outFromNs == "" { - outFromNs = req.Selector.Resource.Namespace - } - - if outFromNs != "" { - labels = append(labels, promLabel(namespaceLabel, outFromNs)) - } - if req.Selector.Resource.Name != "" { - labels = append(labels, promNameLabel(req.GetOutFromResource())) - } + case *pb.StatSummaryRequest_FromResource: + aggregations = promDstLabelNames(req.Selector.Resource) + labels = labels.Merge(promLabels(out.FromResource)) + labels = labels.Merge(promDirectionLabels("outbound")) default: - direction = "inbound" + aggregations = promLabelNames(req.Selector.Resource) + labels = labels.Merge(promLabels(req.Selector.Resource)) + labels = labels.Merge(promDirectionLabels("inbound")) + } - // it's weird to check this again outside the switch, but including this code - // in the other three switch branches is very repetitive - if req.GetOutFromResource() == nil { - if req.Selector.Resource.Namespace != "" { - labels = append(labels, promNamespaceLabel(req.Selector.Resource)) - } - if req.Selector.Resource.Name != "" { - labels = append(labels, promNameLabel(req.Selector.Resource)) - } - } - labels = append(labels, promLabel("direction", direction)) - - return strings.Join(labels, ",") + return labels, aggregations } -func (s *grpcServer) getRequests(ctx context.Context, reqLabels string, groupBy string, timeWindow string) (map[string]*pb.BasicStats, error) { +func (s *grpcServer) getRequests(ctx context.Context, req *pb.StatSummaryRequest, timeWindow string) (map[string]*pb.BasicStats, error) { + reqLabels, groupBy := buildRequestLabels(req) resultChan := make(chan promResult) // kick off 4 asynchronous queries: 1 request volume + 3 latency @@ -226,7 +258,7 @@ func (s *grpcServer) getRequests(ctx context.Context, reqLabels string, groupBy for i := 0; i < len(promTypes); i++ { result := <-resultChan if result.err != nil { - log.Errorf("queryProm failed with: %s", err) + log.Errorf("queryProm failed with: %s", result.err) err = result.err } else { results = append(results, result) @@ -239,12 +271,12 @@ func (s *grpcServer) getRequests(ctx context.Context, reqLabels string, groupBy return processRequests(results, groupBy), nil } -func processRequests(results []promResult, labelSelector string) map[string]*pb.BasicStats { +func processRequests(results []promResult, groupBy model.LabelNames) map[string]*pb.BasicStats { basicStats := make(map[string]*pb.BasicStats) for _, result := range results { for _, sample := range result.vec { - label := metricToKey(sample.Metric, labelSelector) + label := metricToKey(sample.Metric, groupBy) if basicStats[label] == nil { basicStats[label] = &pb.BasicStats{} } @@ -275,18 +307,15 @@ func processRequests(results []promResult, labelSelector string) map[string]*pb. return basicStats } -func metricToKey(metric model.Metric, labelSelector string) string { - if metric[model.LabelName(namespaceLabel)] == "" { - return string(metric[model.LabelName(labelSelector)]) +func metricToKey(metric model.Metric, groupBy model.LabelNames) string { + values := []string{} + for _, k := range groupBy { + values = append(values, string(metric[k])) } - - return fmt.Sprintf("%s/%s", - metric[model.LabelName(namespaceLabel)], - metric[model.LabelName(labelSelector)], - ) + return strings.Join(values, "/") } -func (s *grpcServer) getDeployments(res *pb.Resource) ([]*appsv1beta2.Deployment, map[string]*meshedCount, error) { +func (s *grpcServer) getDeployments(res *pb.Resource) (map[string]metav1.ObjectMeta, map[string]*meshedCount, error) { var err error var deployments []*appsv1beta2.Deployment @@ -305,22 +334,59 @@ func (s *grpcServer) getDeployments(res *pb.Resource) ([]*appsv1beta2.Deployment } meshedPodCount := make(map[string]*meshedCount) + deploymentMap := make(map[string]metav1.ObjectMeta) for _, deployment := range deployments { - meshCount, err := s.getMeshedPodCount(deployment.Namespace, deployment) + key, err := cache.MetaNamespaceKeyFunc(deployment) if err != nil { return nil, nil, err } - key, err := cache.MetaNamespaceKeyFunc(deployment) + deploymentMap[key] = deployment.ObjectMeta + + meshCount, err := s.getMeshedPodCount(deployment.Namespace, deployment) if err != nil { return nil, nil, err } meshedPodCount[key] = meshCount } - return deployments, meshedPodCount, nil + return deploymentMap, meshedPodCount, nil +} + +func (s *grpcServer) getNamespaces(res *pb.Resource) (map[string]metav1.ObjectMeta, map[string]*meshedCount, error) { + var err error + var namespaces []*apiv1.Namespace + + if res.Name == "" { + namespaces, err = s.namespaceLister.List(labels.Everything()) + } else { + var namespace *apiv1.Namespace + namespace, err = s.namespaceLister.Get(res.Name) + namespaces = []*apiv1.Namespace{namespace} + } + + if err != nil { + return nil, nil, err + } + + meshedPodCount := make(map[string]*meshedCount) + namespaceMap := make(map[string]metav1.ObjectMeta) + for _, namespace := range namespaces { + key, err := cache.MetaNamespaceKeyFunc(namespace) + if err != nil { + return nil, nil, err + } + namespaceMap[key] = namespace.ObjectMeta + + meshCount, err := s.getMeshedPodCount(namespace.Name, namespace) + if err != nil { + return nil, nil, err + } + meshedPodCount[key] = meshCount + } + + return namespaceMap, meshedPodCount, nil } -// this takes a long time for namespaces with many pods func (s *grpcServer) getMeshedPodCount(namespace string, obj runtime.Object) (*meshedCount, error) { selector, err := getSelectorFromObject(obj) if err != nil { @@ -350,6 +416,9 @@ func isInMesh(pod *apiv1.Pod) bool { func getSelectorFromObject(obj runtime.Object) (labels.Selector, error) { switch typed := obj.(type) { + case *apiv1.Namespace: + return labels.Everything(), nil + case *appsv1beta2.Deployment: return labels.Set(typed.Spec.Selector.MatchLabels).AsSelector(), nil @@ -364,7 +433,7 @@ func (s *grpcServer) queryProm(ctx context.Context, query string) (model.Vector, // single data point (aka summary) query res, err := s.prometheusAPI.Query(ctx, query, time.Time{}) if err != nil { - log.Errorf("Query(%+v, %+v) failed with: %+v", query, err) + log.Errorf("Query(%+v) failed with: %+v", query, err) return nil, err } log.Debugf("Query response: %+v", res) diff --git a/controller/api/public/stat_summary_test.go b/controller/api/public/stat_summary_test.go index 8aa9e1a90..c98d446c5 100644 --- a/controller/api/public/stat_summary_test.go +++ b/controller/api/public/stat_summary_test.go @@ -136,6 +136,7 @@ metadata: clientSet := fake.NewSimpleClientset(k8sObjs...) sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute) + namespaceInformer := sharedInformers.Core().V1().Namespaces() deployInformer := sharedInformers.Apps().V1beta2().Deployments() replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets() podInformer := sharedInformers.Core().V1().Pods() @@ -143,6 +144,7 @@ metadata: fakeGrpcServer := newGrpcServer( &MockProm{Res: exp.promRes}, tap.NewTapClient(nil), + namespaceInformer.Lister(), deployInformer.Lister(), replicaSetInformer.Lister(), podInformer.Lister(), @@ -153,6 +155,7 @@ metadata: sharedInformers.Start(stopCh) if !cache.WaitForCacheSync( stopCh, + namespaceInformer.Informer().HasSynced, deployInformer.Informer().HasSynced, replicaSetInformer.Informer().HasSynced, podInformer.Informer().HasSynced, @@ -211,6 +214,7 @@ metadata: fakeGrpcServer := newGrpcServer( &MockProm{Res: exp.promRes}, tap.NewTapClient(nil), + sharedInformers.Core().V1().Namespaces().Lister(), sharedInformers.Apps().V1beta2().Deployments().Lister(), sharedInformers.Apps().V1beta2().ReplicaSets().Lister(), sharedInformers.Core().V1().Pods().Lister(), diff --git a/controller/api/util/api_utils.go b/controller/api/util/api_utils.go index 3178e0f78..d622d1345 100644 --- a/controller/api/util/api_utils.go +++ b/controller/api/util/api_utils.go @@ -45,16 +45,16 @@ func GetWindowString(timeWindow pb.TimeWindow) (string, error) { var defaultMetricTimeWindow = pb.TimeWindow_ONE_MIN type StatSummaryRequestParams struct { - TimeWindow string - Namespace string - ResourceType string - ResourceName string - OutToNamespace string - OutToType string - OutToName string - OutFromNamespace string - OutFromType string - OutFromName string + TimeWindow string + Namespace string + ResourceType string + ResourceName string + ToNamespace string + ToType string + ToName string + FromNamespace string + FromType string + FromName string } func BuildStatSummaryRequest(p StatSummaryRequestParams) (*pb.StatSummaryRequest, error) { @@ -83,34 +83,50 @@ func BuildStatSummaryRequest(p StatSummaryRequestParams) (*pb.StatSummaryRequest TimeWindow: window, } - if p.OutToName != "" || p.OutToType != "" || p.OutToNamespace != "" { - if p.OutToNamespace == "" { - p.OutToNamespace = p.Namespace + if p.ToName != "" || p.ToType != "" || p.ToNamespace != "" { + if p.ToNamespace == "" { + p.ToNamespace = p.Namespace + } + if p.ToType == "" { + p.ToType = resourceType } - outToResource := pb.StatSummaryRequest_OutToResource{ - OutToResource: &pb.Resource{ - Namespace: p.OutToNamespace, - Type: p.OutToType, - Name: p.OutToName, + toType, err := k8s.CanonicalKubernetesNameFromFriendlyName(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 = &outToResource + statRequest.Outbound = &toResource } - if p.OutFromName != "" || p.OutFromType != "" || p.OutFromNamespace != "" { - if p.OutFromNamespace == "" { - p.OutFromNamespace = p.Namespace + if p.FromName != "" || p.FromType != "" || p.FromNamespace != "" { + if p.FromNamespace == "" { + p.FromNamespace = p.Namespace + } + if p.FromType == "" { + p.FromType = resourceType } - outFromResource := pb.StatSummaryRequest_OutFromResource{ - OutFromResource: &pb.Resource{ - Namespace: p.OutFromNamespace, - Type: p.OutFromType, - Name: p.OutFromName, + fromType, err := k8s.CanonicalKubernetesNameFromFriendlyName(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 = &outFromResource + statRequest.Outbound = &fromResource } return statRequest, nil diff --git a/controller/cmd/public-api/main.go b/controller/cmd/public-api/main.go index 737789479..3a0d5cbaa 100644 --- a/controller/cmd/public-api/main.go +++ b/controller/cmd/public-api/main.go @@ -57,6 +57,9 @@ func main() { sharedInformers := informers.NewSharedInformerFactory(k8sClient, 10*time.Minute) + namespaceInformer := sharedInformers.Core().V1().Namespaces() + namespaceInformerSynced := namespaceInformer.Informer().HasSynced + deployInformer := sharedInformers.Apps().V1beta2().Deployments() deployInformerSynced := deployInformer.Informer().HasSynced @@ -77,6 +80,7 @@ func main() { *addr, prometheusClient, tapClient, + namespaceInformer.Lister(), deployInformer.Lister(), replicaSetInformer.Lister(), podInformer.Lister(), @@ -91,6 +95,7 @@ func main() { log.Infof("waiting for caches to sync") if !cache.WaitForCacheSync( ctx.Done(), + namespaceInformerSynced, deployInformerSynced, replicaSetInformerSynced, podInformerSynced, diff --git a/controller/gen/public/api.pb.go b/controller/gen/public/api.pb.go index ecd47631c..869315e2d 100644 --- a/controller/gen/public/api.pb.go +++ b/controller/gen/public/api.pb.go @@ -487,8 +487,8 @@ type StatSummaryRequest struct { TimeWindow TimeWindow `protobuf:"varint,2,opt,name=time_window,json=timeWindow,enum=conduit.public.TimeWindow" json:"time_window,omitempty"` // Types that are valid to be assigned to Outbound: // *StatSummaryRequest_None - // *StatSummaryRequest_OutToResource - // *StatSummaryRequest_OutFromResource + // *StatSummaryRequest_ToResource + // *StatSummaryRequest_FromResource Outbound isStatSummaryRequest_Outbound `protobuf_oneof:"outbound"` } @@ -502,16 +502,16 @@ type isStatSummaryRequest_Outbound interface{ isStatSummaryRequest_Outbound() } type StatSummaryRequest_None struct { None *Empty `protobuf:"bytes,3,opt,name=none,oneof"` } -type StatSummaryRequest_OutToResource struct { - OutToResource *Resource `protobuf:"bytes,4,opt,name=out_to_resource,json=outToResource,oneof"` +type StatSummaryRequest_ToResource struct { + ToResource *Resource `protobuf:"bytes,4,opt,name=to_resource,json=toResource,oneof"` } -type StatSummaryRequest_OutFromResource struct { - OutFromResource *Resource `protobuf:"bytes,5,opt,name=out_from_resource,json=outFromResource,oneof"` +type StatSummaryRequest_FromResource struct { + FromResource *Resource `protobuf:"bytes,5,opt,name=from_resource,json=fromResource,oneof"` } -func (*StatSummaryRequest_None) isStatSummaryRequest_Outbound() {} -func (*StatSummaryRequest_OutToResource) isStatSummaryRequest_Outbound() {} -func (*StatSummaryRequest_OutFromResource) isStatSummaryRequest_Outbound() {} +func (*StatSummaryRequest_None) isStatSummaryRequest_Outbound() {} +func (*StatSummaryRequest_ToResource) isStatSummaryRequest_Outbound() {} +func (*StatSummaryRequest_FromResource) isStatSummaryRequest_Outbound() {} func (m *StatSummaryRequest) GetOutbound() isStatSummaryRequest_Outbound { if m != nil { @@ -541,16 +541,16 @@ func (m *StatSummaryRequest) GetNone() *Empty { return nil } -func (m *StatSummaryRequest) GetOutToResource() *Resource { - if x, ok := m.GetOutbound().(*StatSummaryRequest_OutToResource); ok { - return x.OutToResource +func (m *StatSummaryRequest) GetToResource() *Resource { + if x, ok := m.GetOutbound().(*StatSummaryRequest_ToResource); ok { + return x.ToResource } return nil } -func (m *StatSummaryRequest) GetOutFromResource() *Resource { - if x, ok := m.GetOutbound().(*StatSummaryRequest_OutFromResource); ok { - return x.OutFromResource +func (m *StatSummaryRequest) GetFromResource() *Resource { + if x, ok := m.GetOutbound().(*StatSummaryRequest_FromResource); ok { + return x.FromResource } return nil } @@ -559,8 +559,8 @@ func (m *StatSummaryRequest) GetOutFromResource() *Resource { func (*StatSummaryRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { return _StatSummaryRequest_OneofMarshaler, _StatSummaryRequest_OneofUnmarshaler, _StatSummaryRequest_OneofSizer, []interface{}{ (*StatSummaryRequest_None)(nil), - (*StatSummaryRequest_OutToResource)(nil), - (*StatSummaryRequest_OutFromResource)(nil), + (*StatSummaryRequest_ToResource)(nil), + (*StatSummaryRequest_FromResource)(nil), } } @@ -573,14 +573,14 @@ func _StatSummaryRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) erro if err := b.EncodeMessage(x.None); err != nil { return err } - case *StatSummaryRequest_OutToResource: + case *StatSummaryRequest_ToResource: b.EncodeVarint(4<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.OutToResource); err != nil { + if err := b.EncodeMessage(x.ToResource); err != nil { return err } - case *StatSummaryRequest_OutFromResource: + case *StatSummaryRequest_FromResource: b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.OutFromResource); err != nil { + if err := b.EncodeMessage(x.FromResource); err != nil { return err } case nil: @@ -601,21 +601,21 @@ func _StatSummaryRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *p err := b.DecodeMessage(msg) m.Outbound = &StatSummaryRequest_None{msg} return true, err - case 4: // outbound.out_to_resource + case 4: // outbound.to_resource if wire != proto.WireBytes { return true, proto.ErrInternalBadWireType } msg := new(Resource) err := b.DecodeMessage(msg) - m.Outbound = &StatSummaryRequest_OutToResource{msg} + m.Outbound = &StatSummaryRequest_ToResource{msg} return true, err - case 5: // outbound.out_from_resource + case 5: // outbound.from_resource if wire != proto.WireBytes { return true, proto.ErrInternalBadWireType } msg := new(Resource) err := b.DecodeMessage(msg) - m.Outbound = &StatSummaryRequest_OutFromResource{msg} + m.Outbound = &StatSummaryRequest_FromResource{msg} return true, err default: return false, nil @@ -631,13 +631,13 @@ func _StatSummaryRequest_OneofSizer(msg proto.Message) (n int) { n += proto.SizeVarint(3<<3 | proto.WireBytes) n += proto.SizeVarint(uint64(s)) n += s - case *StatSummaryRequest_OutToResource: - s := proto.Size(x.OutToResource) + case *StatSummaryRequest_ToResource: + s := proto.Size(x.ToResource) n += proto.SizeVarint(4<<3 | proto.WireBytes) n += proto.SizeVarint(uint64(s)) n += s - case *StatSummaryRequest_OutFromResource: - s := proto.Size(x.OutFromResource) + case *StatSummaryRequest_FromResource: + s := proto.Size(x.FromResource) n += proto.SizeVarint(5<<3 | proto.WireBytes) n += proto.SizeVarint(uint64(s)) n += s @@ -1238,81 +1238,80 @@ var _Api_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("public/api.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1204 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x5d, 0x6f, 0x1b, 0x45, - 0x17, 0xb6, 0xd7, 0x4e, 0xb2, 0x3e, 0x8e, 0xd3, 0x74, 0xf2, 0xbe, 0x68, 0x6b, 0xa0, 0x32, 0xdb, - 0x12, 0x22, 0x3e, 0x9c, 0xc8, 0x10, 0x89, 0xb4, 0x42, 0x90, 0xa4, 0x2e, 0x8e, 0xd4, 0x26, 0xd6, - 0xc4, 0x7c, 0x48, 0x5c, 0x58, 0xe3, 0xdd, 0x49, 0xbc, 0x64, 0x77, 0x67, 0xba, 0x33, 0x4b, 0xc8, - 0xff, 0xe0, 0x82, 0x3b, 0xee, 0xf8, 0x27, 0x48, 0xfc, 0x00, 0xf8, 0x37, 0x5c, 0xa0, 0xf9, 0xd8, - 0x8d, 0xe3, 0x24, 0x6d, 0x55, 0xae, 0x3c, 0xe7, 0x39, 0xcf, 0x9c, 0x39, 0xe7, 0x39, 0x67, 0xd6, - 0x03, 0xab, 0x3c, 0x9f, 0xc4, 0x51, 0xb0, 0x49, 0x78, 0xd4, 0xe5, 0x19, 0x93, 0x0c, 0xad, 0x04, - 0x2c, 0x0d, 0xf3, 0x48, 0x76, 0x8d, 0xa7, 0x7d, 0xff, 0x94, 0xb1, 0xd3, 0x98, 0x6e, 0x6a, 0xef, - 0x24, 0x3f, 0xd9, 0x0c, 0xf3, 0x8c, 0xc8, 0x88, 0xa5, 0x86, 0xdf, 0x5e, 0x0b, 0x58, 0x92, 0xb0, - 0x74, 0xd3, 0xfc, 0x58, 0xf0, 0xa1, 0x05, 0xa7, 0x94, 0xc4, 0x72, 0x1a, 0x4c, 0x69, 0x70, 0x36, - 0xbb, 0x36, 0x2c, 0x7f, 0x09, 0x16, 0xfa, 0x09, 0x97, 0x17, 0xfe, 0x0b, 0x68, 0x7e, 0x4b, 0x33, - 0x11, 0xb1, 0xf4, 0x20, 0x3d, 0x61, 0xe8, 0x1d, 0x68, 0x9c, 0x32, 0x0b, 0x78, 0xd5, 0x4e, 0x75, - 0xa3, 0x81, 0x2f, 0x01, 0xe5, 0x9d, 0xe4, 0x51, 0x1c, 0x3e, 0x21, 0x92, 0x7a, 0x8e, 0xf1, 0x96, - 0x00, 0x5a, 0x87, 0x95, 0x8c, 0xc6, 0x94, 0x08, 0x5a, 0x04, 0xa8, 0x69, 0xca, 0x1c, 0xea, 0x3f, - 0x86, 0xd5, 0x67, 0x91, 0x90, 0x43, 0x16, 0x0a, 0x4c, 0x05, 0x67, 0xa9, 0xa0, 0xe8, 0x03, 0xa8, - 0x73, 0x16, 0x0a, 0xaf, 0xda, 0xa9, 0x6d, 0x34, 0x7b, 0x6b, 0xdd, 0xab, 0x4a, 0x74, 0x87, 0x2c, - 0xc4, 0x9a, 0xe0, 0xff, 0xea, 0x40, 0x6d, 0xc8, 0x42, 0x84, 0xa0, 0x9e, 0x92, 0x84, 0xda, 0x1c, - 0xf5, 0x1a, 0xfd, 0x0f, 0x16, 0x38, 0x0b, 0x0f, 0x86, 0x36, 0x35, 0x63, 0xa0, 0xfb, 0x00, 0x21, - 0xe5, 0x31, 0xbb, 0x48, 0x68, 0x2a, 0x6d, 0x4a, 0x33, 0x08, 0x7a, 0x0b, 0x16, 0x85, 0x24, 0x32, - 0x17, 0x5e, 0x5d, 0xfb, 0xac, 0xa5, 0xa2, 0x91, 0x30, 0xa4, 0xa1, 0xb7, 0xd0, 0xa9, 0x6e, 0xb8, - 0xd8, 0x18, 0x68, 0x1f, 0xee, 0x88, 0x28, 0x0d, 0xe8, 0x33, 0x22, 0x24, 0xa6, 0x9c, 0x65, 0xd2, - 0x5b, 0xec, 0x54, 0x37, 0x9a, 0xbd, 0x7b, 0x5d, 0xd3, 0xad, 0x6e, 0xd1, 0xad, 0xee, 0x13, 0xdb, - 0x2d, 0x3c, 0xbf, 0x03, 0x6d, 0xc1, 0x5a, 0xc0, 0x52, 0x99, 0xb1, 0x38, 0xa6, 0xd9, 0x21, 0x49, - 0xa8, 0xe0, 0x24, 0xa0, 0xde, 0x92, 0x3e, 0xff, 0x26, 0x17, 0xf2, 0x61, 0xd9, 0xc2, 0xc3, 0x98, - 0xa4, 0xd4, 0x73, 0x75, 0x4e, 0x57, 0x30, 0xff, 0x77, 0x07, 0x60, 0x44, 0x38, 0xa6, 0x2f, 0x72, - 0x2a, 0x24, 0x42, 0x50, 0xe3, 0x2c, 0x34, 0x02, 0x0d, 0x2a, 0x58, 0x19, 0xa8, 0x73, 0x45, 0x0b, - 0xc7, 0xba, 0xe6, 0xd4, 0x48, 0xc8, 0xcf, 0x98, 0x0b, 0xad, 0x94, 0x83, 0xad, 0xa5, 0x70, 0xc9, - 0x86, 0xaa, 0x5c, 0xa5, 0x52, 0x0b, 0x5b, 0x4b, 0xf5, 0x41, 0xb2, 0x83, 0xa1, 0x16, 0xa9, 0x81, - 0xf5, 0x1a, 0xb5, 0xc1, 0x3d, 0xc9, 0x58, 0x32, 0x2c, 0xc4, 0x69, 0xe1, 0xd2, 0x56, 0x71, 0xd4, - 0xfa, 0x60, 0x68, 0xab, 0xb5, 0x96, 0xee, 0x42, 0x30, 0xa5, 0x89, 0x29, 0x4d, 0x75, 0x41, 0x5b, - 0x3a, 0x1f, 0x2a, 0xa7, 0x2c, 0xf4, 0x1a, 0x06, 0x37, 0x96, 0x1a, 0x45, 0x92, 0xcb, 0x29, 0xcb, - 0x22, 0x79, 0xe1, 0x81, 0x19, 0xc5, 0x12, 0x50, 0x59, 0x71, 0x22, 0xa7, 0x5e, 0xd3, 0x64, 0xa5, - 0xd6, 0x7b, 0x2e, 0x2c, 0x4a, 0x92, 0x9d, 0x52, 0xe9, 0x77, 0xc0, 0xdd, 0xe5, 0x51, 0x3f, 0xcb, - 0x58, 0xa6, 0xba, 0x4c, 0xd5, 0xc2, 0x0e, 0x92, 0x31, 0xfc, 0x21, 0xb8, 0x98, 0x0a, 0x96, 0x67, - 0x01, 0x55, 0x27, 0xa5, 0x65, 0x8b, 0xec, 0x95, 0x28, 0x01, 0x5d, 0xff, 0x05, 0x2f, 0x6e, 0x83, - 0x5e, 0x97, 0xb3, 0x59, 0xbb, 0x9c, 0x4d, 0x9f, 0xc3, 0xdd, 0x22, 0xe2, 0x31, 0x8d, 0x69, 0xa0, - 0x06, 0x03, 0x7d, 0x06, 0x6e, 0x66, 0x41, 0x1d, 0xb9, 0xd9, 0xf3, 0xe6, 0x27, 0xbf, 0xd8, 0x84, - 0x4b, 0x26, 0x7a, 0x1f, 0x56, 0x62, 0x32, 0xa1, 0xf1, 0x58, 0xe8, 0x40, 0x2c, 0xb3, 0x87, 0xb7, - 0x34, 0x7a, 0x6c, 0x41, 0xff, 0x07, 0x68, 0x15, 0x9b, 0x4d, 0xa9, 0x6f, 0x76, 0x5a, 0x29, 0x90, - 0x33, 0x2b, 0xd0, 0xdf, 0x0e, 0xa0, 0x63, 0x49, 0xe4, 0x71, 0x9e, 0x24, 0x24, 0xbb, 0x28, 0x66, - 0xee, 0x0b, 0x70, 0xcb, 0xa4, 0xcc, 0x11, 0xef, 0xdd, 0x76, 0x44, 0xa9, 0x02, 0x2e, 0xb7, 0xa0, - 0xc7, 0xd0, 0x94, 0x51, 0x42, 0xc7, 0xe7, 0x51, 0x1a, 0xb2, 0x73, 0x7d, 0xe2, 0x4a, 0xaf, 0x3d, - 0x1f, 0x61, 0x14, 0x25, 0xf4, 0x3b, 0xcd, 0xc0, 0x20, 0xcb, 0x35, 0xfa, 0x08, 0xea, 0x29, 0x4b, - 0x8d, 0xea, 0xcd, 0xde, 0xff, 0xe7, 0x77, 0xe9, 0xcf, 0xdd, 0xa0, 0x82, 0x35, 0x09, 0xed, 0xc1, - 0x1d, 0x96, 0xcb, 0xb1, 0x64, 0xe3, 0x52, 0x92, 0xfa, 0xcb, 0x25, 0x19, 0x54, 0x70, 0x8b, 0xe5, - 0x72, 0xc4, 0xca, 0xc1, 0x78, 0x0a, 0x77, 0x55, 0x0c, 0x35, 0xc0, 0x97, 0x51, 0x16, 0x5e, 0x19, - 0x45, 0x1d, 0xfc, 0x34, 0x63, 0x49, 0x01, 0xed, 0x01, 0xb8, 0x2c, 0x97, 0x13, 0x96, 0xa7, 0xa1, - 0xff, 0x57, 0x15, 0xd6, 0xae, 0xe8, 0x6a, 0xbf, 0x8f, 0x9f, 0x83, 0xc3, 0xce, 0xac, 0xa4, 0xeb, - 0xf3, 0xc1, 0x6f, 0xd8, 0xd0, 0x3d, 0x3a, 0x1b, 0x54, 0xb0, 0xc3, 0xce, 0xd0, 0xf6, 0x6c, 0xff, - 0x9a, 0xbd, 0x77, 0x6f, 0xcb, 0x4c, 0xcf, 0xc8, 0xa0, 0x62, 0x1b, 0xdc, 0xfe, 0x0a, 0x9c, 0xa3, - 0x33, 0xf4, 0x08, 0x9a, 0xea, 0x6b, 0x38, 0x96, 0x64, 0x12, 0xd3, 0xe2, 0xeb, 0x7c, 0xef, 0xa6, - 0xf3, 0x47, 0x8a, 0x81, 0x41, 0x14, 0x4b, 0xa1, 0xca, 0xca, 0x6c, 0x36, 0xfe, 0x9f, 0x55, 0x80, - 0x3d, 0x22, 0xa2, 0x40, 0x51, 0x05, 0x7a, 0x00, 0x2d, 0x91, 0x07, 0x01, 0x15, 0x62, 0x1c, 0xb0, - 0x3c, 0x95, 0xba, 0xb0, 0x3a, 0x5e, 0xb6, 0xe0, 0xbe, 0xc2, 0x14, 0xe9, 0x84, 0x44, 0x71, 0x9e, - 0x51, 0x4b, 0x72, 0x0c, 0xc9, 0x82, 0x86, 0xf4, 0x50, 0xdd, 0x05, 0x49, 0xd3, 0xe0, 0x62, 0x9c, - 0x88, 0x31, 0xdf, 0xde, 0xd2, 0xed, 0xaf, 0xe3, 0x65, 0x8b, 0x3e, 0x17, 0xc3, 0xed, 0xad, 0x79, - 0xd6, 0xce, 0xb6, 0x6e, 0xf6, 0x15, 0xd6, 0xce, 0xf6, 0x35, 0xd6, 0x8e, 0x6e, 0xe6, 0x55, 0xd6, - 0x8e, 0xff, 0x5b, 0x0d, 0x1a, 0x65, 0xc1, 0x68, 0x17, 0x1a, 0x9c, 0x85, 0xe3, 0xd3, 0x8c, 0xe5, - 0xdc, 0xb6, 0xc7, 0xbf, 0x55, 0x1e, 0xf5, 0x37, 0xf6, 0xb5, 0x62, 0x0e, 0x2a, 0xd8, 0xe5, 0x76, - 0xdd, 0xfe, 0xc3, 0x01, 0xb7, 0x70, 0xa0, 0x47, 0x50, 0xcf, 0xd8, 0x79, 0xa1, 0xf4, 0xfa, 0xab, - 0x43, 0x75, 0x31, 0x3b, 0xc7, 0x7a, 0x4f, 0xfb, 0x9f, 0x2a, 0xd4, 0x30, 0x3b, 0x7f, 0xc3, 0x7b, - 0xfe, 0x9f, 0xee, 0xde, 0x06, 0xac, 0x26, 0x54, 0x4c, 0x69, 0x38, 0x56, 0x6a, 0x98, 0x76, 0x99, - 0x46, 0xac, 0x18, 0x7c, 0xc8, 0x42, 0xd3, 0xb0, 0x75, 0xb8, 0x23, 0x99, 0x24, 0xf1, 0x0c, 0xd1, - 0xf4, 0xa2, 0xa5, 0xe1, 0x92, 0xb7, 0x05, 0x0b, 0x6a, 0x96, 0x84, 0xbd, 0x50, 0xd7, 0x12, 0xb9, - 0x9c, 0x26, 0x6c, 0x88, 0x7b, 0x4b, 0xb0, 0xa0, 0xc7, 0xf4, 0xc3, 0x5d, 0x80, 0xcb, 0x34, 0x51, - 0x13, 0x96, 0x46, 0xfd, 0xc3, 0xf1, 0x71, 0x7f, 0x7f, 0xb5, 0xa2, 0x8c, 0xa3, 0xc3, 0xfe, 0xf8, - 0xf9, 0xc1, 0xe1, 0x6a, 0xb5, 0xf0, 0x28, 0xc3, 0x41, 0xcb, 0xe0, 0x2a, 0xcf, 0xe0, 0xe8, 0x1b, - 0xbc, 0x5a, 0xeb, 0xfd, 0x52, 0x83, 0xda, 0x2e, 0x8f, 0xd0, 0xf7, 0xd0, 0x9c, 0xb9, 0x5c, 0xc8, - 0x7f, 0xe9, 0xcd, 0xd3, 0x9f, 0xc0, 0xf6, 0x83, 0xd7, 0xb8, 0x9d, 0x7e, 0x05, 0x7d, 0x09, 0x4b, - 0xc5, 0xab, 0xea, 0xe6, 0x4f, 0x55, 0xfb, 0xed, 0x79, 0x78, 0xe6, 0x9d, 0xe6, 0x57, 0x50, 0x1f, - 0xdc, 0xe2, 0x15, 0x75, 0x5b, 0x84, 0xce, 0x3c, 0x3c, 0xff, 0xec, 0xf2, 0x2b, 0xe8, 0x47, 0x68, - 0x1c, 0xd3, 0xf8, 0x64, 0x5f, 0xbd, 0x0d, 0xd1, 0xc7, 0xe5, 0x06, 0xfb, 0xa4, 0x9c, 0x7d, 0x38, - 0x96, 0xb4, 0xa2, 0xd2, 0x4f, 0x5e, 0x93, 0x3d, 0x53, 0x73, 0x6d, 0x44, 0x38, 0xba, 0x3e, 0x54, - 0xe5, 0xa3, 0xa5, 0xed, 0xcd, 0xc7, 0x1c, 0x11, 0xde, 0xff, 0x89, 0xa6, 0xd2, 0xaf, 0x6c, 0x55, - 0x27, 0x8b, 0xfa, 0x6d, 0xf5, 0xe9, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x43, 0x1e, 0x96, 0xb2, - 0x3b, 0x0b, 0x00, 0x00, + // 1189 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x6e, 0x1b, 0xc5, + 0x17, 0xb7, 0xd7, 0x4e, 0xb2, 0x3e, 0x8e, 0xd3, 0xfc, 0x27, 0x7f, 0xd0, 0xd6, 0x40, 0x65, 0xb6, + 0x25, 0x44, 0x7c, 0x38, 0x91, 0x21, 0x12, 0x69, 0x85, 0x4a, 0x92, 0x5a, 0x24, 0x52, 0x9b, 0x58, + 0x93, 0xf0, 0x21, 0x71, 0x61, 0x8d, 0x77, 0x27, 0xf1, 0x92, 0xdd, 0x9d, 0xe9, 0xce, 0x2c, 0x21, + 0xef, 0xc1, 0x05, 0x77, 0xdc, 0xf1, 0x26, 0x08, 0x1e, 0x80, 0xc7, 0xe1, 0x02, 0xcd, 0xc7, 0x6e, + 0xec, 0x6d, 0xd2, 0x56, 0xe5, 0xca, 0xe7, 0xfc, 0xce, 0xef, 0x9c, 0x39, 0x5f, 0xb3, 0x1e, 0x58, + 0xe5, 0xf9, 0x24, 0x8e, 0x82, 0x4d, 0xc2, 0xa3, 0x3e, 0xcf, 0x98, 0x64, 0x68, 0x25, 0x60, 0x69, + 0x98, 0x47, 0xb2, 0x6f, 0x2c, 0xdd, 0x7b, 0xe7, 0x8c, 0x9d, 0xc7, 0x74, 0x53, 0x5b, 0x27, 0xf9, + 0xd9, 0x66, 0x98, 0x67, 0x44, 0x46, 0x2c, 0x35, 0xfc, 0xee, 0x5a, 0xc0, 0x92, 0x84, 0xa5, 0x9b, + 0xe6, 0xc7, 0x82, 0x0f, 0x2c, 0x38, 0xa5, 0x24, 0x96, 0xd3, 0x60, 0x4a, 0x83, 0x8b, 0x59, 0xd9, + 0xb0, 0xfc, 0x25, 0x58, 0x18, 0x26, 0x5c, 0x5e, 0xf9, 0xcf, 0xa1, 0xfd, 0x2d, 0xcd, 0x44, 0xc4, + 0xd2, 0xc3, 0xf4, 0x8c, 0xa1, 0x77, 0xa1, 0x75, 0xce, 0x2c, 0xe0, 0xd5, 0x7b, 0xf5, 0x8d, 0x16, + 0xbe, 0x06, 0x94, 0x75, 0x92, 0x47, 0x71, 0xf8, 0x84, 0x48, 0xea, 0x39, 0xc6, 0x5a, 0x02, 0x68, + 0x1d, 0x56, 0x32, 0x1a, 0x53, 0x22, 0x68, 0x11, 0xa0, 0xa1, 0x29, 0x15, 0xd4, 0x7f, 0x04, 0xab, + 0x4f, 0x23, 0x21, 0x47, 0x2c, 0x14, 0x98, 0x0a, 0xce, 0x52, 0x41, 0xd1, 0x87, 0xd0, 0xe4, 0x2c, + 0x14, 0x5e, 0xbd, 0xd7, 0xd8, 0x68, 0x0f, 0xd6, 0xfa, 0xf3, 0x9d, 0xe8, 0x8f, 0x58, 0x88, 0x35, + 0xc1, 0xff, 0xd5, 0x81, 0xc6, 0x88, 0x85, 0x08, 0x41, 0x33, 0x25, 0x09, 0xb5, 0x39, 0x6a, 0x19, + 0xfd, 0x1f, 0x16, 0x38, 0x0b, 0x0f, 0x47, 0x36, 0x35, 0xa3, 0xa0, 0x7b, 0x00, 0x21, 0xe5, 0x31, + 0xbb, 0x4a, 0x68, 0x2a, 0x6d, 0x4a, 0x33, 0x08, 0x7a, 0x1b, 0x16, 0x85, 0x24, 0x32, 0x17, 0x5e, + 0x53, 0xdb, 0xac, 0xa6, 0xa2, 0x91, 0x30, 0xa4, 0xa1, 0xb7, 0xd0, 0xab, 0x6f, 0xb8, 0xd8, 0x28, + 0x68, 0x1f, 0xee, 0x88, 0x28, 0x0d, 0xe8, 0x53, 0x22, 0x24, 0xa6, 0x9c, 0x65, 0xd2, 0x5b, 0xec, + 0xd5, 0x37, 0xda, 0x83, 0xbb, 0x7d, 0x33, 0xad, 0x7e, 0x31, 0xad, 0xfe, 0x13, 0x3b, 0x2d, 0x5c, + 0xf5, 0x40, 0x5b, 0xb0, 0x16, 0xb0, 0x54, 0x66, 0x2c, 0x8e, 0x69, 0x76, 0x44, 0x12, 0x2a, 0x38, + 0x09, 0xa8, 0xb7, 0xa4, 0xcf, 0xbf, 0xc9, 0x84, 0x7c, 0x58, 0xb6, 0xf0, 0x28, 0x26, 0x29, 0xf5, + 0x5c, 0x9d, 0xd3, 0x1c, 0xe6, 0xff, 0xee, 0x00, 0x9c, 0x12, 0x8e, 0xe9, 0xf3, 0x9c, 0x0a, 0x89, + 0x10, 0x34, 0x38, 0x0b, 0x4d, 0x83, 0x0e, 0x6a, 0x58, 0x29, 0xa8, 0x37, 0xd7, 0x0b, 0xc7, 0x9a, + 0x2a, 0xdd, 0x48, 0xc8, 0xcf, 0x98, 0x0b, 0xdd, 0x29, 0x07, 0x5b, 0x4d, 0xe1, 0x92, 0x8d, 0x54, + 0xb9, 0xaa, 0x4b, 0x1d, 0x6c, 0x35, 0x35, 0x07, 0xc9, 0x0e, 0x47, 0xba, 0x49, 0x2d, 0xac, 0x65, + 0xd4, 0x05, 0xf7, 0x2c, 0x63, 0xc9, 0xa8, 0x68, 0x4e, 0x07, 0x97, 0xba, 0x8a, 0xa3, 0xe4, 0xc3, + 0x91, 0xad, 0xd6, 0x6a, 0x7a, 0x0a, 0xc1, 0x94, 0x26, 0xa6, 0x34, 0x35, 0x05, 0xad, 0xe9, 0x7c, + 0xa8, 0x9c, 0xb2, 0xd0, 0x6b, 0x19, 0xdc, 0x68, 0x6a, 0x15, 0x49, 0x2e, 0xa7, 0x2c, 0x8b, 0xe4, + 0x95, 0x07, 0x66, 0x15, 0x4b, 0x40, 0x65, 0xc5, 0x89, 0x9c, 0x7a, 0x6d, 0x93, 0x95, 0x92, 0xf7, + 0x5c, 0x58, 0x94, 0x24, 0x3b, 0xa7, 0xd2, 0xef, 0x81, 0xbb, 0xcb, 0xa3, 0x61, 0x96, 0xb1, 0x4c, + 0x4d, 0x99, 0x2a, 0xc1, 0x2e, 0x92, 0x51, 0xfc, 0x11, 0xb8, 0x98, 0x0a, 0x96, 0x67, 0x01, 0x55, + 0x27, 0xa5, 0xe5, 0x88, 0xec, 0x95, 0x28, 0x01, 0x5d, 0xff, 0x15, 0x2f, 0x6e, 0x83, 0x96, 0xcb, + 0xdd, 0x6c, 0x5c, 0xef, 0xa6, 0xcf, 0xe1, 0x7f, 0x45, 0xc4, 0x13, 0x1a, 0xd3, 0x40, 0x2d, 0x06, + 0xfa, 0x1c, 0xdc, 0xcc, 0x82, 0x3a, 0x72, 0x7b, 0xe0, 0x55, 0x37, 0xbf, 0x70, 0xc2, 0x25, 0x13, + 0x7d, 0x00, 0x2b, 0x31, 0x99, 0xd0, 0x78, 0x2c, 0x74, 0x20, 0x96, 0xd9, 0xc3, 0x3b, 0x1a, 0x3d, + 0xb1, 0xa0, 0xff, 0x03, 0x74, 0x0a, 0x67, 0x53, 0xea, 0x9b, 0x9d, 0x56, 0x36, 0xc8, 0x99, 0x6d, + 0xd0, 0x9f, 0x0e, 0xa0, 0x13, 0x49, 0xe4, 0x49, 0x9e, 0x24, 0x24, 0xbb, 0x2a, 0x76, 0xee, 0x4b, + 0x70, 0xcb, 0xa4, 0xcc, 0x11, 0xef, 0xdf, 0x76, 0x44, 0xd9, 0x05, 0x5c, 0xba, 0xa0, 0x47, 0xd0, + 0x96, 0x51, 0x42, 0xc7, 0x97, 0x51, 0x1a, 0xb2, 0x4b, 0x7d, 0xe2, 0xca, 0xa0, 0x5b, 0x8d, 0x70, + 0x1a, 0x25, 0xf4, 0x3b, 0xcd, 0xc0, 0x20, 0x4b, 0x19, 0x7d, 0x0c, 0xcd, 0x94, 0xa5, 0xa6, 0xeb, + 0xed, 0xc1, 0x5b, 0x55, 0x2f, 0xfd, 0xb9, 0x3b, 0xa8, 0x61, 0x4d, 0xd2, 0x27, 0xb1, 0x71, 0xd9, + 0x8e, 0xe6, 0xcb, 0xdb, 0xa1, 0xee, 0x88, 0x64, 0xe5, 0x46, 0x3c, 0x86, 0x8e, 0xda, 0xda, 0x6b, + 0xf7, 0x85, 0x57, 0xba, 0x2f, 0x2b, 0x87, 0x42, 0xdf, 0x03, 0x70, 0x59, 0x2e, 0x27, 0x2c, 0x4f, + 0x43, 0xff, 0xef, 0x3a, 0xac, 0xcd, 0x75, 0xd2, 0x7e, 0x11, 0xbf, 0x00, 0x87, 0x5d, 0xd8, 0x26, + 0xae, 0x57, 0x23, 0xdf, 0xe0, 0xd0, 0x3f, 0xbe, 0x38, 0xa8, 0x61, 0x87, 0x5d, 0xa0, 0xed, 0xd9, + 0x89, 0xb5, 0x07, 0xef, 0xdd, 0x96, 0x96, 0xde, 0x8a, 0x83, 0x9a, 0x1d, 0x69, 0xf7, 0x2b, 0x70, + 0x8e, 0x2f, 0xd0, 0x43, 0x68, 0xab, 0xef, 0xdf, 0x58, 0x92, 0x49, 0x4c, 0x8b, 0xef, 0xf1, 0xdd, + 0x9b, 0xce, 0x3f, 0x55, 0x0c, 0x0c, 0xa2, 0x10, 0x85, 0x2a, 0x2b, 0xb3, 0xd9, 0xf8, 0x7f, 0xd5, + 0x01, 0xf6, 0x88, 0x88, 0x02, 0x45, 0x15, 0xe8, 0x3e, 0x74, 0x44, 0x1e, 0x04, 0x54, 0x88, 0x71, + 0xc0, 0xf2, 0x54, 0xea, 0xc2, 0x9a, 0x78, 0xd9, 0x82, 0xfb, 0x0a, 0x53, 0xa4, 0x33, 0x12, 0xc5, + 0x79, 0x46, 0x2d, 0xc9, 0x31, 0x24, 0x0b, 0x1a, 0xd2, 0x03, 0xb5, 0xfd, 0x92, 0xa6, 0xc1, 0xd5, + 0x38, 0x11, 0x63, 0xbe, 0xbd, 0xa5, 0x07, 0xde, 0xc4, 0xcb, 0x16, 0x7d, 0x26, 0x46, 0xdb, 0x5b, + 0x55, 0xd6, 0xce, 0xb6, 0x1e, 0xf1, 0x1c, 0x6b, 0x67, 0xfb, 0x05, 0xd6, 0x8e, 0x9e, 0xe4, 0x3c, + 0x6b, 0xc7, 0xff, 0xad, 0x01, 0xad, 0xb2, 0x60, 0xb4, 0x0b, 0x2d, 0xce, 0xc2, 0xf1, 0x79, 0xc6, + 0x72, 0x6e, 0xc7, 0xe3, 0xdf, 0xda, 0x1e, 0xf5, 0xc7, 0xf5, 0xb5, 0x62, 0x1e, 0xd4, 0xb0, 0xcb, + 0xad, 0xdc, 0xfd, 0xc3, 0x01, 0xb7, 0x30, 0xa0, 0x87, 0xd0, 0xcc, 0xd8, 0x65, 0xd1, 0xe9, 0xf5, + 0x57, 0x87, 0xea, 0x63, 0x76, 0x89, 0xb5, 0x4f, 0xf7, 0x9f, 0x3a, 0x34, 0x30, 0xbb, 0x7c, 0xc3, + 0x9b, 0xfd, 0x9f, 0x6e, 0xdb, 0x06, 0xac, 0x26, 0x54, 0x4c, 0x69, 0x38, 0x56, 0xdd, 0x30, 0xe3, + 0x32, 0x83, 0x58, 0x31, 0xf8, 0x88, 0x85, 0x66, 0x60, 0xeb, 0x70, 0x47, 0x32, 0x49, 0xe2, 0x19, + 0xa2, 0x99, 0x45, 0x47, 0xc3, 0x25, 0x6f, 0x0b, 0x16, 0xd4, 0x2e, 0x09, 0x7b, 0x9b, 0x5e, 0x48, + 0xe4, 0x7a, 0x9b, 0xb0, 0x21, 0xee, 0x2d, 0xc1, 0x82, 0x5e, 0xd3, 0x8f, 0x76, 0x01, 0xae, 0xd3, + 0x44, 0x6d, 0x58, 0x3a, 0x1d, 0x1e, 0x8d, 0x4f, 0x86, 0xfb, 0xab, 0x35, 0xa5, 0x1c, 0x1f, 0x0d, + 0xc7, 0xcf, 0x0e, 0x8f, 0x56, 0xeb, 0x85, 0x45, 0x29, 0x0e, 0x5a, 0x06, 0x57, 0x59, 0x0e, 0x8e, + 0xbf, 0xc1, 0xab, 0x8d, 0xc1, 0x2f, 0x0d, 0x68, 0xec, 0xf2, 0x08, 0x7d, 0x0f, 0xed, 0x99, 0xcb, + 0x85, 0xfc, 0x97, 0xde, 0x3c, 0xfd, 0xd1, 0xeb, 0xde, 0x7f, 0x8d, 0xdb, 0xe9, 0xd7, 0xd0, 0x63, + 0x58, 0x2a, 0xde, 0x51, 0x37, 0x7f, 0x9c, 0xba, 0xef, 0x54, 0xe1, 0x99, 0x97, 0x99, 0x5f, 0x43, + 0x43, 0x70, 0x8b, 0x77, 0xd3, 0x6d, 0x11, 0x7a, 0x55, 0xb8, 0xfa, 0xd0, 0xf2, 0x6b, 0xe8, 0x47, + 0x68, 0x9d, 0xd0, 0xf8, 0x6c, 0x5f, 0xbd, 0x06, 0xd1, 0x27, 0xa5, 0x83, 0x7d, 0x44, 0xce, 0x3e, + 0x15, 0x4b, 0x5a, 0x51, 0xe9, 0xa7, 0xaf, 0xc9, 0x9e, 0xa9, 0xb9, 0x71, 0x4a, 0x38, 0x7a, 0x71, + 0xa9, 0xca, 0x67, 0x4a, 0xd7, 0xab, 0xc6, 0x3c, 0x25, 0x7c, 0xf8, 0x13, 0x4d, 0xa5, 0x5f, 0xdb, + 0xaa, 0x4f, 0x16, 0xf5, 0x6b, 0xea, 0xb3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x9d, 0x3d, + 0xa9, 0x2d, 0x0b, 0x00, 0x00, } diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index f49deb0ee..c7da4867a 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -10,6 +10,7 @@ import ( const ( KubernetesDeployments = "deployments" + KubernetesNamespaces = "namespaces" KubernetesPods = "pods" ) @@ -58,6 +59,8 @@ func CanonicalKubernetesNameFromFriendlyName(friendlyName string) (string, error switch friendlyName { case "deploy", "deployment", "deployments": return KubernetesDeployments, nil + case "ns", "namespace", "namespaces": + return KubernetesNamespaces, nil case "po", "pod", "pods": return KubernetesPods, nil } diff --git a/proto/public/api.proto b/proto/public/api.proto index 50f030b90..b83456b68 100644 --- a/proto/public/api.proto +++ b/proto/public/api.proto @@ -80,8 +80,8 @@ message StatSummaryRequest { oneof outbound { Empty none = 3; - Resource out_to_resource = 4; - Resource out_from_resource = 5; + Resource to_resource = 4; + Resource from_resource = 5; } } diff --git a/web/srv/api_handlers.go b/web/srv/api_handlers.go index 2f9cb622e..35d735523 100644 --- a/web/srv/api_handlers.go +++ b/web/srv/api_handlers.go @@ -71,16 +71,16 @@ func (h *handler) handleApiPods(w http.ResponseWriter, req *http.Request, p http func (h *handler) handleApiStat(w http.ResponseWriter, req *http.Request, p httprouter.Params) { requestParams := util.StatSummaryRequestParams{ - TimeWindow: req.FormValue("window"), - ResourceName: req.FormValue("resource_name"), - ResourceType: req.FormValue("resource_type"), - Namespace: req.FormValue("namespace"), - OutToName: req.FormValue("out_to_name"), - OutToType: req.FormValue("out_to_type"), - OutToNamespace: req.FormValue("out_to_namespace"), - OutFromName: req.FormValue("out_from_name"), - OutFromType: req.FormValue("out_from_type"), - OutFromNamespace: req.FormValue("out_from_namespace"), + TimeWindow: req.FormValue("window"), + ResourceName: req.FormValue("resource_name"), + ResourceType: req.FormValue("resource_type"), + Namespace: req.FormValue("namespace"), + ToName: req.FormValue("to_name"), + ToType: req.FormValue("to_type"), + ToNamespace: req.FormValue("to_namespace"), + FromName: req.FormValue("from_name"), + FromType: req.FormValue("from_type"), + FromNamespace: req.FormValue("from_namespace"), } // default to returning deployment stats