Add namespace as a resource type in public-api (#760)

* Add namespace as a resource type in public-api

The cli and public-api only supported deployments as a resource type.

This change adds support for namespace as a resource type in the cli and
public-api. This also change includes:
- cli statsummary now prints `-`'s when objects are not in the mesh
- cli statsummary prints `No resources found.` when applicable
- removed `out-` from cli statsummary flags, and analagous proto changes
- switched public-api to use native prometheus label types
- misc error handling and logging fixes

Part of #627

Signed-off-by: Andrew Seigner <siggy@buoyant.io>

* Refactor filter and groupby label formulation

Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>

* Rename stat_summary.go to stat.go in cli

Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>

* Update rbac privileges for namespace stats

Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
This commit is contained in:
Andrew Seigner 2018-04-13 16:53:01 -07:00 committed by GitHub
parent cc44db054f
commit 77fb6d3709
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 401 additions and 267 deletions

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"sort" "sort"
"strings" "strings"
"text/tabwriter" "text/tabwriter"
@ -13,13 +14,14 @@ import (
"github.com/prometheus/common/log" "github.com/prometheus/common/log"
"github.com/runconduit/conduit/controller/api/util" "github.com/runconduit/conduit/controller/api/util"
pb "github.com/runconduit/conduit/controller/gen/public" pb "github.com/runconduit/conduit/controller/gen/public"
"github.com/runconduit/conduit/pkg/k8s"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
) )
var timeWindow, namespace, resourceType, resourceName string var timeWindow, namespace, resourceType, resourceName string
var outToNamespace, outToType, outToName string var toNamespace, toType, toName string
var outFromNamespace, outFromType, outFromName string var fromNamespace, fromType, fromName string
var allNamespaces bool var allNamespaces bool
var statCmd = &cobra.Command{ var statCmd = &cobra.Command{
@ -29,7 +31,8 @@ var statCmd = &cobra.Command{
Valid resource types include: 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. 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`, 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 conduit stat deployments -n test
# Get the hello1 deployment in the test namespace. # 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), Args: cobra.RangeArgs(1, 2),
ValidArgs: []string{"deployment"}, ValidArgs: []string{k8s.KubernetesDeployments, k8s.KubernetesNamespaces},
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
switch len(args) { switch len(args) {
case 1: case 1:
@ -71,12 +80,12 @@ func init() {
RootCmd.AddCommand(statCmd) RootCmd.AddCommand(statCmd)
statCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "default", "Namespace of the specified resource") 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().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(&toName, "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(&toNamespace, "to-namespace", "", "Sets the namespace used to lookup the \"--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(&toType, "to-resource", "", "Sets the resource type used to lookup the \"--to\" resource; by default the RESOURCETYPE is used")
statCmd.PersistentFlags().StringVar(&outFromName, "out-from", "", "If present, restricts outbound stats to the specified resource name") statCmd.PersistentFlags().StringVar(&fromName, "from", "", "If present, restricts outbound stats from 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(&fromNamespace, "from-namespace", "", "Sets the namespace used from lookup the \"--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(&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") 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 const padding = 3
type row struct { type rowStats struct {
meshed string
requestRate float64 requestRate float64
successRate float64 successRate float64
latencyP50 uint64 latencyP50 uint64
@ -119,6 +127,11 @@ type row struct {
latencyP99 uint64 latencyP99 uint64
} }
type row struct {
meshed string
*rowStats
}
func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) { func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) {
nameHeader := "NAME" nameHeader := "NAME"
maxNameLength := len(nameHeader) maxNameLength := len(nameHeader)
@ -147,15 +160,22 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) {
} }
if r.Stats != nil { if r.Stats != nil {
stats[key].requestRate = getRequestRate(*r) stats[key].rowStats = &rowStats{
stats[key].successRate = getSuccessRate(*r) requestRate: getRequestRate(*r),
stats[key].latencyP50 = r.Stats.LatencyMsP50 successRate: getSuccessRate(*r),
stats[key].latencyP95 = r.Stats.LatencyMsP95 latencyP50: r.Stats.LatencyMsP50,
stats[key].latencyP99 = r.Stats.LatencyMsP99 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) headers := make([]string, 0)
if allNamespaces { if allNamespaces {
headers = append(headers, headers = append(headers,
@ -170,6 +190,7 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) {
"LATENCY_P95", "LATENCY_P95",
"LATENCY_P99\t", // trailing \t is required to format last column "LATENCY_P99\t", // trailing \t is required to format last column
}...) }...)
fmt.Fprintln(w, strings.Join(headers, "\t")) fmt.Fprintln(w, strings.Join(headers, "\t"))
sortedKeys := sortStatsKeys(stats) sortedKeys := sortStatsKeys(stats)
@ -179,23 +200,32 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) {
name := parts[1] name := parts[1]
values := make([]interface{}, 0) values := make([]interface{}, 0)
templateString := "%s\t%s\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t\n" 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 { if allNamespaces {
values = append(values, values = append(values,
namespace+strings.Repeat(" ", maxNamespaceLength-len(namespace))) namespace+strings.Repeat(" ", maxNamespaceLength-len(namespace)))
templateString = "%s\t" + templateString templateString = "%s\t" + templateString
templateStringEmpty = "%s\t" + templateStringEmpty
} }
values = append(values, []interface{}{ values = append(values, []interface{}{
name + strings.Repeat(" ", maxNameLength-len(name)), name + strings.Repeat(" ", maxNameLength-len(name)),
stats[key].meshed, 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{ requestParams := util.StatSummaryRequestParams{
TimeWindow: timeWindow, TimeWindow: timeWindow,
ResourceName: resourceName, ResourceName: resourceName,
ResourceType: resourceType, ResourceType: resourceType,
Namespace: targetNamespace, Namespace: targetNamespace,
OutToName: outToName, ToName: toName,
OutToType: outToType, ToType: toType,
OutToNamespace: outToNamespace, ToNamespace: toNamespace,
OutFromName: outFromName, FromName: fromName,
OutFromType: outFromType, FromType: fromType,
OutFromNamespace: outFromNamespace, FromNamespace: fromNamespace,
} }
return util.BuildStatSummaryRequest(requestParams) return util.BuildStatSummaryRequest(requestParams)

View File

@ -23,7 +23,7 @@ rules:
resources: ["deployments", "replicasets"] resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
- apiGroups: [""] - apiGroups: [""]
resources: ["pods", "endpoints", "services"] resources: ["pods", "endpoints", "services", "namespaces"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
--- ---

View File

@ -23,7 +23,7 @@ rules:
resources: ["deployments", "replicasets"] resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
- apiGroups: [""] - apiGroups: [""]
resources: ["pods", "endpoints", "services"] resources: ["pods", "endpoints", "services", "namespaces"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
--- ---

View File

@ -26,7 +26,7 @@ rules:
resources: ["deployments", "replicasets"] resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
- apiGroups: [""] - apiGroups: [""]
resources: ["pods", "endpoints", "services"] resources: ["pods", "endpoints", "services", "namespaces"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
--- ---

View File

@ -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 { func (c *grpcOverHttpClient) apiRequest(ctx context.Context, endpoint string, req proto.Message, protoResponse proto.Message) error {
url := c.endpointNameToPublicApiUrl(endpoint) 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) httpRsp, err := c.post(ctx, url, req)
if err != nil { if err != nil {
return err return err

View File

@ -24,6 +24,7 @@ type (
grpcServer struct { grpcServer struct {
prometheusAPI promv1.API prometheusAPI promv1.API
tapClient tapPb.TapClient tapClient tapPb.TapClient
namespaceLister corelisters.NamespaceLister
deployLister applisters.DeploymentLister deployLister applisters.DeploymentLister
replicaSetLister applisters.ReplicaSetLister replicaSetLister applisters.ReplicaSetLister
podLister corelisters.PodLister podLister corelisters.PodLister
@ -43,6 +44,7 @@ const (
func newGrpcServer( func newGrpcServer(
promAPI promv1.API, promAPI promv1.API,
tapClient tapPb.TapClient, tapClient tapPb.TapClient,
namespaceLister corelisters.NamespaceLister,
deployLister applisters.DeploymentLister, deployLister applisters.DeploymentLister,
replicaSetLister applisters.ReplicaSetLister, replicaSetLister applisters.ReplicaSetLister,
podLister corelisters.PodLister, podLister corelisters.PodLister,
@ -52,6 +54,7 @@ func newGrpcServer(
return &grpcServer{ return &grpcServer{
prometheusAPI: promAPI, prometheusAPI: promAPI,
tapClient: tapClient, tapClient: tapClient,
namespaceLister: namespaceLister,
deployLister: deployLister, deployLister: deployLister,
replicaSetLister: replicaSetLister, replicaSetLister: replicaSetLister,
podLister: podLister, podLister: podLister,

View File

@ -166,6 +166,7 @@ spec:
clientSet := fake.NewSimpleClientset(k8sObjs...) clientSet := fake.NewSimpleClientset(k8sObjs...)
sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute) sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute)
namespaceInformer := sharedInformers.Core().V1().Namespaces()
deployInformer := sharedInformers.Apps().V1beta2().Deployments() deployInformer := sharedInformers.Apps().V1beta2().Deployments()
replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets() replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets()
podInformer := sharedInformers.Core().V1().Pods() podInformer := sharedInformers.Core().V1().Pods()
@ -173,6 +174,7 @@ spec:
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes}, &MockProm{Res: exp.promRes},
tap.NewTapClient(nil), tap.NewTapClient(nil),
namespaceInformer.Lister(),
deployInformer.Lister(), deployInformer.Lister(),
replicaSetInformer.Lister(), replicaSetInformer.Lister(),
podInformer.Lister(), podInformer.Lister(),
@ -183,6 +185,7 @@ spec:
sharedInformers.Start(stopCh) sharedInformers.Start(stopCh)
if !cache.WaitForCacheSync( if !cache.WaitForCacheSync(
stopCh, stopCh,
namespaceInformer.Informer().HasSynced,
deployInformer.Informer().HasSynced, deployInformer.Informer().HasSynced,
replicaSetInformer.Informer().HasSynced, replicaSetInformer.Informer().HasSynced,
podInformer.Informer().HasSynced, podInformer.Informer().HasSynced,

View File

@ -199,6 +199,7 @@ func NewServer(
addr string, addr string,
prometheusClient promApi.Client, prometheusClient promApi.Client,
tapClient tapPb.TapClient, tapClient tapPb.TapClient,
namespaceLister corelisters.NamespaceLister,
deployLister applisters.DeploymentLister, deployLister applisters.DeploymentLister,
replicaSetLister applisters.ReplicaSetLister, replicaSetLister applisters.ReplicaSetLister,
podLister corelisters.PodLister, podLister corelisters.PodLister,
@ -209,6 +210,7 @@ func NewServer(
grpcServer: newGrpcServer( grpcServer: newGrpcServer(
promv1.NewAPI(prometheusClient), promv1.NewAPI(prometheusClient),
tapClient, tapClient,
namespaceLister,
deployLister, deployLister,
replicaSetLister, replicaSetLister,
podLister, podLister,

View File

@ -2,7 +2,6 @@ package public
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math" "math"
"strings" "strings"
@ -15,6 +14,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
appsv1beta2 "k8s.io/api/apps/v1beta2" appsv1beta2 "k8s.io/api/apps/v1beta2"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
@ -28,22 +28,24 @@ type promResult struct {
} }
const ( const (
reqQuery = "sum(increase(response_total{%s}[%s])) by (%s, namespace, classification)" reqQuery = "sum(increase(response_total%s[%s])) by (%s, classification)"
latencyQuantileQuery = "histogram_quantile(%s, sum(irate(response_latency_ms_bucket{%s}[%s])) by (le, namespace, %s))" latencyQuantileQuery = "histogram_quantile(%s, sum(irate(response_latency_ms_bucket%s[%s])) by (le, %s))"
promRequests = promType("QUERY_REQUESTS") promRequests = promType("QUERY_REQUESTS")
promLatencyP50 = promType("0.5") promLatencyP50 = promType("0.5")
promLatencyP95 = promType("0.95") promLatencyP95 = promType("0.95")
promLatencyP99 = promType("0.99") promLatencyP99 = promType("0.99")
namespaceLabel = "namespace" namespaceLabel = model.LabelName("namespace")
dstNamespaceLabel = model.LabelName("dst_namespace")
) )
var ( var (
promTypes = []promType{promRequests, promLatencyP50, promLatencyP95, promLatencyP99} promTypes = []promType{promRequests, promLatencyP50, promLatencyP95, promLatencyP99}
k8sResourceTypesToPromLabels = map[string]string{ k8sResourceTypesToPromLabels = map[string]model.LabelName{
k8s.KubernetesDeployments: "deployment", 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) { 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 { switch req.Selector.Resource.Type {
case k8s.KubernetesDeployments: 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: 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) rows := make([]*pb.StatTable_PodGroup_Row, 0)
timeWindow, err := apiUtil.GetWindowString(req.TimeWindow) timeWindow, err := apiUtil.GetWindowString(req.TimeWindow)
@ -69,23 +87,29 @@ func (s *grpcServer) deploymentQuery(ctx context.Context, req *pb.StatSummaryReq
return nil, err return nil, err
} }
deployments, meshCount, err := s.getDeployments(req.Selector.Resource) requestMetrics, err := s.getRequests(ctx, req, timeWindow)
if err != nil { if err != nil {
return nil, err return nil, err
} }
requestLabels := buildRequestLabels(req) var keys []string
groupBy := promResourceType(req.Selector.Resource)
requestMetrics, err := s.getRequests(ctx, requestLabels, groupBy, timeWindow) if req.GetOutbound() == nil || req.GetNone() != nil {
if err != nil { // if this request doesn't have outbound filtering, return all rows
return nil, err 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 { for _, key := range keys {
key, err := cache.MetaNamespaceKeyFunc(resource) resource, ok := objects[key]
if err != nil { if !ok {
return nil, err continue
} }
row := pb.StatTable_PodGroup_Row{ row := pb.StatTable_PodGroup_Row{
@ -125,74 +149,82 @@ func (s *grpcServer) deploymentQuery(ctx context.Context, req *pb.StatSummaryReq
return &rsp, nil return &rsp, nil
} }
func promLabel(key, val string) string { func promLabelNames(resource *pb.Resource) model.LabelNames {
return fmt.Sprintf("%s=\"%s\"", key, val) names := model.LabelNames{namespaceLabel}
if resource.Type != k8s.KubernetesNamespaces {
names = append(names, promResourceType(resource))
}
return names
} }
func promNameLabel(resource *pb.Resource) string { func promDstLabelNames(resource *pb.Resource) model.LabelNames {
return promLabel(promResourceType(resource), resource.Name) names := model.LabelNames{dstNamespaceLabel}
if resource.Type != k8s.KubernetesNamespaces {
names = append(names, "dst_"+promResourceType(resource))
}
return names
} }
func promNamespaceLabel(resource *pb.Resource) string { func promLabels(resource *pb.Resource) model.LabelSet {
return promLabel(namespaceLabel, resource.Namespace) 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 { func promDstLabels(resource *pb.Resource) model.LabelSet {
return []string{ set := model.LabelSet{}
promLabel("dst_"+namespaceLabel, resource.Namespace), if resource.Name != "" {
promLabel("dst_"+promResourceType(resource), 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] return k8sResourceTypesToPromLabels[resource.Type]
} }
func buildRequestLabels(req *pb.StatSummaryRequest) string { func buildRequestLabels(req *pb.StatSummaryRequest) (model.LabelSet, model.LabelNames) {
labels := []string{} labels := model.LabelSet{}
aggregations := model.LabelNames{}
var direction string switch out := req.Outbound.(type) {
switch req.Outbound.(type) { case *pb.StatSummaryRequest_ToResource:
case *pb.StatSummaryRequest_OutToResource: aggregations = promLabelNames(req.Selector.Resource)
direction = "outbound" labels = labels.Merge(promDstLabels(out.ToResource))
labels = append(labels, promDstLabels(req.GetOutToResource())...) labels = labels.Merge(promLabels(req.Selector.Resource))
labels = labels.Merge(promDirectionLabels("outbound"))
case *pb.StatSummaryRequest_OutFromResource: case *pb.StatSummaryRequest_FromResource:
direction = "outbound" aggregations = promDstLabelNames(req.Selector.Resource)
labels = append(labels, promDstLabels(req.Selector.Resource)...) labels = labels.Merge(promLabels(out.FromResource))
labels = labels.Merge(promDirectionLabels("outbound"))
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()))
}
default: 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 return labels, aggregations
// 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, ",")
} }
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) resultChan := make(chan promResult)
// kick off 4 asynchronous queries: 1 request volume + 3 latency // 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++ { for i := 0; i < len(promTypes); i++ {
result := <-resultChan result := <-resultChan
if result.err != nil { if result.err != nil {
log.Errorf("queryProm failed with: %s", err) log.Errorf("queryProm failed with: %s", result.err)
err = result.err err = result.err
} else { } else {
results = append(results, result) results = append(results, result)
@ -239,12 +271,12 @@ func (s *grpcServer) getRequests(ctx context.Context, reqLabels string, groupBy
return processRequests(results, groupBy), nil 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) basicStats := make(map[string]*pb.BasicStats)
for _, result := range results { for _, result := range results {
for _, sample := range result.vec { for _, sample := range result.vec {
label := metricToKey(sample.Metric, labelSelector) label := metricToKey(sample.Metric, groupBy)
if basicStats[label] == nil { if basicStats[label] == nil {
basicStats[label] = &pb.BasicStats{} basicStats[label] = &pb.BasicStats{}
} }
@ -275,18 +307,15 @@ func processRequests(results []promResult, labelSelector string) map[string]*pb.
return basicStats return basicStats
} }
func metricToKey(metric model.Metric, labelSelector string) string { func metricToKey(metric model.Metric, groupBy model.LabelNames) string {
if metric[model.LabelName(namespaceLabel)] == "" { values := []string{}
return string(metric[model.LabelName(labelSelector)]) for _, k := range groupBy {
values = append(values, string(metric[k]))
} }
return strings.Join(values, "/")
return fmt.Sprintf("%s/%s",
metric[model.LabelName(namespaceLabel)],
metric[model.LabelName(labelSelector)],
)
} }
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 err error
var deployments []*appsv1beta2.Deployment var deployments []*appsv1beta2.Deployment
@ -305,22 +334,59 @@ func (s *grpcServer) getDeployments(res *pb.Resource) ([]*appsv1beta2.Deployment
} }
meshedPodCount := make(map[string]*meshedCount) meshedPodCount := make(map[string]*meshedCount)
deploymentMap := make(map[string]metav1.ObjectMeta)
for _, deployment := range deployments { for _, deployment := range deployments {
meshCount, err := s.getMeshedPodCount(deployment.Namespace, deployment) key, err := cache.MetaNamespaceKeyFunc(deployment)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
key, err := cache.MetaNamespaceKeyFunc(deployment) deploymentMap[key] = deployment.ObjectMeta
meshCount, err := s.getMeshedPodCount(deployment.Namespace, deployment)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
meshedPodCount[key] = meshCount 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) { func (s *grpcServer) getMeshedPodCount(namespace string, obj runtime.Object) (*meshedCount, error) {
selector, err := getSelectorFromObject(obj) selector, err := getSelectorFromObject(obj)
if err != nil { if err != nil {
@ -350,6 +416,9 @@ func isInMesh(pod *apiv1.Pod) bool {
func getSelectorFromObject(obj runtime.Object) (labels.Selector, error) { func getSelectorFromObject(obj runtime.Object) (labels.Selector, error) {
switch typed := obj.(type) { switch typed := obj.(type) {
case *apiv1.Namespace:
return labels.Everything(), nil
case *appsv1beta2.Deployment: case *appsv1beta2.Deployment:
return labels.Set(typed.Spec.Selector.MatchLabels).AsSelector(), nil 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 // single data point (aka summary) query
res, err := s.prometheusAPI.Query(ctx, query, time.Time{}) res, err := s.prometheusAPI.Query(ctx, query, time.Time{})
if err != nil { 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 return nil, err
} }
log.Debugf("Query response: %+v", res) log.Debugf("Query response: %+v", res)

View File

@ -136,6 +136,7 @@ metadata:
clientSet := fake.NewSimpleClientset(k8sObjs...) clientSet := fake.NewSimpleClientset(k8sObjs...)
sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute) sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute)
namespaceInformer := sharedInformers.Core().V1().Namespaces()
deployInformer := sharedInformers.Apps().V1beta2().Deployments() deployInformer := sharedInformers.Apps().V1beta2().Deployments()
replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets() replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets()
podInformer := sharedInformers.Core().V1().Pods() podInformer := sharedInformers.Core().V1().Pods()
@ -143,6 +144,7 @@ metadata:
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes}, &MockProm{Res: exp.promRes},
tap.NewTapClient(nil), tap.NewTapClient(nil),
namespaceInformer.Lister(),
deployInformer.Lister(), deployInformer.Lister(),
replicaSetInformer.Lister(), replicaSetInformer.Lister(),
podInformer.Lister(), podInformer.Lister(),
@ -153,6 +155,7 @@ metadata:
sharedInformers.Start(stopCh) sharedInformers.Start(stopCh)
if !cache.WaitForCacheSync( if !cache.WaitForCacheSync(
stopCh, stopCh,
namespaceInformer.Informer().HasSynced,
deployInformer.Informer().HasSynced, deployInformer.Informer().HasSynced,
replicaSetInformer.Informer().HasSynced, replicaSetInformer.Informer().HasSynced,
podInformer.Informer().HasSynced, podInformer.Informer().HasSynced,
@ -211,6 +214,7 @@ metadata:
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes}, &MockProm{Res: exp.promRes},
tap.NewTapClient(nil), tap.NewTapClient(nil),
sharedInformers.Core().V1().Namespaces().Lister(),
sharedInformers.Apps().V1beta2().Deployments().Lister(), sharedInformers.Apps().V1beta2().Deployments().Lister(),
sharedInformers.Apps().V1beta2().ReplicaSets().Lister(), sharedInformers.Apps().V1beta2().ReplicaSets().Lister(),
sharedInformers.Core().V1().Pods().Lister(), sharedInformers.Core().V1().Pods().Lister(),

View File

@ -45,16 +45,16 @@ func GetWindowString(timeWindow pb.TimeWindow) (string, error) {
var defaultMetricTimeWindow = pb.TimeWindow_ONE_MIN var defaultMetricTimeWindow = pb.TimeWindow_ONE_MIN
type StatSummaryRequestParams struct { type StatSummaryRequestParams struct {
TimeWindow string TimeWindow string
Namespace string Namespace string
ResourceType string ResourceType string
ResourceName string ResourceName string
OutToNamespace string ToNamespace string
OutToType string ToType string
OutToName string ToName string
OutFromNamespace string FromNamespace string
OutFromType string FromType string
OutFromName string FromName string
} }
func BuildStatSummaryRequest(p StatSummaryRequestParams) (*pb.StatSummaryRequest, error) { func BuildStatSummaryRequest(p StatSummaryRequestParams) (*pb.StatSummaryRequest, error) {
@ -83,34 +83,50 @@ func BuildStatSummaryRequest(p StatSummaryRequestParams) (*pb.StatSummaryRequest
TimeWindow: window, TimeWindow: window,
} }
if p.OutToName != "" || p.OutToType != "" || p.OutToNamespace != "" { if p.ToName != "" || p.ToType != "" || p.ToNamespace != "" {
if p.OutToNamespace == "" { if p.ToNamespace == "" {
p.OutToNamespace = p.Namespace p.ToNamespace = p.Namespace
}
if p.ToType == "" {
p.ToType = resourceType
} }
outToResource := pb.StatSummaryRequest_OutToResource{ toType, err := k8s.CanonicalKubernetesNameFromFriendlyName(p.ToType)
OutToResource: &pb.Resource{ if err != nil {
Namespace: p.OutToNamespace, return nil, err
Type: p.OutToType, }
Name: p.OutToName,
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.FromName != "" || p.FromType != "" || p.FromNamespace != "" {
if p.OutFromNamespace == "" { if p.FromNamespace == "" {
p.OutFromNamespace = p.Namespace p.FromNamespace = p.Namespace
}
if p.FromType == "" {
p.FromType = resourceType
} }
outFromResource := pb.StatSummaryRequest_OutFromResource{ fromType, err := k8s.CanonicalKubernetesNameFromFriendlyName(p.FromType)
OutFromResource: &pb.Resource{ if err != nil {
Namespace: p.OutFromNamespace, return nil, err
Type: p.OutFromType, }
Name: p.OutFromName,
fromResource := pb.StatSummaryRequest_FromResource{
FromResource: &pb.Resource{
Namespace: p.FromNamespace,
Type: fromType,
Name: p.FromName,
}, },
} }
statRequest.Outbound = &outFromResource statRequest.Outbound = &fromResource
} }
return statRequest, nil return statRequest, nil

View File

@ -57,6 +57,9 @@ func main() {
sharedInformers := informers.NewSharedInformerFactory(k8sClient, 10*time.Minute) sharedInformers := informers.NewSharedInformerFactory(k8sClient, 10*time.Minute)
namespaceInformer := sharedInformers.Core().V1().Namespaces()
namespaceInformerSynced := namespaceInformer.Informer().HasSynced
deployInformer := sharedInformers.Apps().V1beta2().Deployments() deployInformer := sharedInformers.Apps().V1beta2().Deployments()
deployInformerSynced := deployInformer.Informer().HasSynced deployInformerSynced := deployInformer.Informer().HasSynced
@ -77,6 +80,7 @@ func main() {
*addr, *addr,
prometheusClient, prometheusClient,
tapClient, tapClient,
namespaceInformer.Lister(),
deployInformer.Lister(), deployInformer.Lister(),
replicaSetInformer.Lister(), replicaSetInformer.Lister(),
podInformer.Lister(), podInformer.Lister(),
@ -91,6 +95,7 @@ func main() {
log.Infof("waiting for caches to sync") log.Infof("waiting for caches to sync")
if !cache.WaitForCacheSync( if !cache.WaitForCacheSync(
ctx.Done(), ctx.Done(),
namespaceInformerSynced,
deployInformerSynced, deployInformerSynced,
replicaSetInformerSynced, replicaSetInformerSynced,
podInformerSynced, podInformerSynced,

View File

@ -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"` 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: // Types that are valid to be assigned to Outbound:
// *StatSummaryRequest_None // *StatSummaryRequest_None
// *StatSummaryRequest_OutToResource // *StatSummaryRequest_ToResource
// *StatSummaryRequest_OutFromResource // *StatSummaryRequest_FromResource
Outbound isStatSummaryRequest_Outbound `protobuf_oneof:"outbound"` Outbound isStatSummaryRequest_Outbound `protobuf_oneof:"outbound"`
} }
@ -502,16 +502,16 @@ type isStatSummaryRequest_Outbound interface{ isStatSummaryRequest_Outbound() }
type StatSummaryRequest_None struct { type StatSummaryRequest_None struct {
None *Empty `protobuf:"bytes,3,opt,name=none,oneof"` None *Empty `protobuf:"bytes,3,opt,name=none,oneof"`
} }
type StatSummaryRequest_OutToResource struct { type StatSummaryRequest_ToResource struct {
OutToResource *Resource `protobuf:"bytes,4,opt,name=out_to_resource,json=outToResource,oneof"` ToResource *Resource `protobuf:"bytes,4,opt,name=to_resource,json=toResource,oneof"`
} }
type StatSummaryRequest_OutFromResource struct { type StatSummaryRequest_FromResource struct {
OutFromResource *Resource `protobuf:"bytes,5,opt,name=out_from_resource,json=outFromResource,oneof"` FromResource *Resource `protobuf:"bytes,5,opt,name=from_resource,json=fromResource,oneof"`
} }
func (*StatSummaryRequest_None) isStatSummaryRequest_Outbound() {} func (*StatSummaryRequest_None) isStatSummaryRequest_Outbound() {}
func (*StatSummaryRequest_OutToResource) isStatSummaryRequest_Outbound() {} func (*StatSummaryRequest_ToResource) isStatSummaryRequest_Outbound() {}
func (*StatSummaryRequest_OutFromResource) isStatSummaryRequest_Outbound() {} func (*StatSummaryRequest_FromResource) isStatSummaryRequest_Outbound() {}
func (m *StatSummaryRequest) GetOutbound() isStatSummaryRequest_Outbound { func (m *StatSummaryRequest) GetOutbound() isStatSummaryRequest_Outbound {
if m != nil { if m != nil {
@ -541,16 +541,16 @@ func (m *StatSummaryRequest) GetNone() *Empty {
return nil return nil
} }
func (m *StatSummaryRequest) GetOutToResource() *Resource { func (m *StatSummaryRequest) GetToResource() *Resource {
if x, ok := m.GetOutbound().(*StatSummaryRequest_OutToResource); ok { if x, ok := m.GetOutbound().(*StatSummaryRequest_ToResource); ok {
return x.OutToResource return x.ToResource
} }
return nil return nil
} }
func (m *StatSummaryRequest) GetOutFromResource() *Resource { func (m *StatSummaryRequest) GetFromResource() *Resource {
if x, ok := m.GetOutbound().(*StatSummaryRequest_OutFromResource); ok { if x, ok := m.GetOutbound().(*StatSummaryRequest_FromResource); ok {
return x.OutFromResource return x.FromResource
} }
return nil 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{}) { 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{}{ return _StatSummaryRequest_OneofMarshaler, _StatSummaryRequest_OneofUnmarshaler, _StatSummaryRequest_OneofSizer, []interface{}{
(*StatSummaryRequest_None)(nil), (*StatSummaryRequest_None)(nil),
(*StatSummaryRequest_OutToResource)(nil), (*StatSummaryRequest_ToResource)(nil),
(*StatSummaryRequest_OutFromResource)(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 { if err := b.EncodeMessage(x.None); err != nil {
return err return err
} }
case *StatSummaryRequest_OutToResource: case *StatSummaryRequest_ToResource:
b.EncodeVarint(4<<3 | proto.WireBytes) b.EncodeVarint(4<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.OutToResource); err != nil { if err := b.EncodeMessage(x.ToResource); err != nil {
return err return err
} }
case *StatSummaryRequest_OutFromResource: case *StatSummaryRequest_FromResource:
b.EncodeVarint(5<<3 | proto.WireBytes) b.EncodeVarint(5<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.OutFromResource); err != nil { if err := b.EncodeMessage(x.FromResource); err != nil {
return err return err
} }
case nil: case nil:
@ -601,21 +601,21 @@ func _StatSummaryRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *p
err := b.DecodeMessage(msg) err := b.DecodeMessage(msg)
m.Outbound = &StatSummaryRequest_None{msg} m.Outbound = &StatSummaryRequest_None{msg}
return true, err return true, err
case 4: // outbound.out_to_resource case 4: // outbound.to_resource
if wire != proto.WireBytes { if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType return true, proto.ErrInternalBadWireType
} }
msg := new(Resource) msg := new(Resource)
err := b.DecodeMessage(msg) err := b.DecodeMessage(msg)
m.Outbound = &StatSummaryRequest_OutToResource{msg} m.Outbound = &StatSummaryRequest_ToResource{msg}
return true, err return true, err
case 5: // outbound.out_from_resource case 5: // outbound.from_resource
if wire != proto.WireBytes { if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType return true, proto.ErrInternalBadWireType
} }
msg := new(Resource) msg := new(Resource)
err := b.DecodeMessage(msg) err := b.DecodeMessage(msg)
m.Outbound = &StatSummaryRequest_OutFromResource{msg} m.Outbound = &StatSummaryRequest_FromResource{msg}
return true, err return true, err
default: default:
return false, nil 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(3<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s)) n += proto.SizeVarint(uint64(s))
n += s n += s
case *StatSummaryRequest_OutToResource: case *StatSummaryRequest_ToResource:
s := proto.Size(x.OutToResource) s := proto.Size(x.ToResource)
n += proto.SizeVarint(4<<3 | proto.WireBytes) n += proto.SizeVarint(4<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s)) n += proto.SizeVarint(uint64(s))
n += s n += s
case *StatSummaryRequest_OutFromResource: case *StatSummaryRequest_FromResource:
s := proto.Size(x.OutFromResource) s := proto.Size(x.FromResource)
n += proto.SizeVarint(5<<3 | proto.WireBytes) n += proto.SizeVarint(5<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s)) n += proto.SizeVarint(uint64(s))
n += s n += s
@ -1238,81 +1238,80 @@ var _Api_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("public/api.proto", fileDescriptor0) } func init() { proto.RegisterFile("public/api.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 1204 bytes of a gzipped FileDescriptorProto // 1189 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x5d, 0x6f, 0x1b, 0x45, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x6e, 0x1b, 0xc5,
0x17, 0xb6, 0xd7, 0x4e, 0xb2, 0x3e, 0x8e, 0xd3, 0x74, 0xf2, 0xbe, 0x68, 0x6b, 0xa0, 0x32, 0xdb, 0x17, 0xb7, 0xd7, 0x4e, 0xb2, 0x3e, 0x8e, 0xd3, 0xfc, 0x27, 0x7f, 0xd0, 0xd6, 0x40, 0x65, 0xb6,
0x12, 0x22, 0x3e, 0x9c, 0xc8, 0x10, 0x89, 0xb4, 0x42, 0x90, 0xa4, 0x2e, 0x8e, 0xd4, 0x26, 0xd6, 0x25, 0x44, 0x7c, 0x38, 0x91, 0x21, 0x12, 0x69, 0x85, 0x4a, 0x92, 0x5a, 0x24, 0x52, 0x9b, 0x58,
0xc4, 0x7c, 0x48, 0x5c, 0x58, 0xe3, 0xdd, 0x49, 0xbc, 0x64, 0x77, 0x67, 0xba, 0x33, 0x4b, 0xc8, 0x93, 0xf0, 0x21, 0x71, 0x61, 0x8d, 0x77, 0x27, 0xf1, 0x92, 0xdd, 0x9d, 0xe9, 0xce, 0x2c, 0x21,
0xff, 0xe0, 0x82, 0x3b, 0xee, 0xf8, 0x27, 0x48, 0xfc, 0x00, 0xf8, 0x37, 0x5c, 0xa0, 0xf9, 0xd8, 0xef, 0xc1, 0x05, 0x77, 0xdc, 0xf1, 0x26, 0x08, 0x1e, 0x80, 0xc7, 0xe1, 0x02, 0xcd, 0xc7, 0x6e,
0x8d, 0xe3, 0x24, 0x6d, 0x55, 0xae, 0x3c, 0xe7, 0x39, 0xcf, 0x9c, 0x39, 0xe7, 0x39, 0x67, 0xd6, 0xec, 0x6d, 0xd2, 0x56, 0xe5, 0xca, 0xe7, 0xfc, 0xce, 0xef, 0x9c, 0x39, 0x5f, 0xb3, 0x1e, 0x58,
0x03, 0xab, 0x3c, 0x9f, 0xc4, 0x51, 0xb0, 0x49, 0x78, 0xd4, 0xe5, 0x19, 0x93, 0x0c, 0xad, 0x04, 0xe5, 0xf9, 0x24, 0x8e, 0x82, 0x4d, 0xc2, 0xa3, 0x3e, 0xcf, 0x98, 0x64, 0x68, 0x25, 0x60, 0x69,
0x2c, 0x0d, 0xf3, 0x48, 0x76, 0x8d, 0xa7, 0x7d, 0xff, 0x94, 0xb1, 0xd3, 0x98, 0x6e, 0x6a, 0xef, 0x98, 0x47, 0xb2, 0x6f, 0x2c, 0xdd, 0x7b, 0xe7, 0x8c, 0x9d, 0xc7, 0x74, 0x53, 0x5b, 0x27, 0xf9,
0x24, 0x3f, 0xd9, 0x0c, 0xf3, 0x8c, 0xc8, 0x88, 0xa5, 0x86, 0xdf, 0x5e, 0x0b, 0x58, 0x92, 0xb0, 0xd9, 0x66, 0x98, 0x67, 0x44, 0x46, 0x2c, 0x35, 0xfc, 0xee, 0x5a, 0xc0, 0x92, 0x84, 0xa5, 0x9b,
0x74, 0xd3, 0xfc, 0x58, 0xf0, 0xa1, 0x05, 0xa7, 0x94, 0xc4, 0x72, 0x1a, 0x4c, 0x69, 0x70, 0x36, 0xe6, 0xc7, 0x82, 0x0f, 0x2c, 0x38, 0xa5, 0x24, 0x96, 0xd3, 0x60, 0x4a, 0x83, 0x8b, 0x59, 0xd9,
0xbb, 0x36, 0x2c, 0x7f, 0x09, 0x16, 0xfa, 0x09, 0x97, 0x17, 0xfe, 0x0b, 0x68, 0x7e, 0x4b, 0x33, 0xb0, 0xfc, 0x25, 0x58, 0x18, 0x26, 0x5c, 0x5e, 0xf9, 0xcf, 0xa1, 0xfd, 0x2d, 0xcd, 0x44, 0xc4,
0x11, 0xb1, 0xf4, 0x20, 0x3d, 0x61, 0xe8, 0x1d, 0x68, 0x9c, 0x32, 0x0b, 0x78, 0xd5, 0x4e, 0x75, 0xd2, 0xc3, 0xf4, 0x8c, 0xa1, 0x77, 0xa1, 0x75, 0xce, 0x2c, 0xe0, 0xd5, 0x7b, 0xf5, 0x8d, 0x16,
0xa3, 0x81, 0x2f, 0x01, 0xe5, 0x9d, 0xe4, 0x51, 0x1c, 0x3e, 0x21, 0x92, 0x7a, 0x8e, 0xf1, 0x96, 0xbe, 0x06, 0x94, 0x75, 0x92, 0x47, 0x71, 0xf8, 0x84, 0x48, 0xea, 0x39, 0xc6, 0x5a, 0x02, 0x68,
0x00, 0x5a, 0x87, 0x95, 0x8c, 0xc6, 0x94, 0x08, 0x5a, 0x04, 0xa8, 0x69, 0xca, 0x1c, 0xea, 0x3f, 0x1d, 0x56, 0x32, 0x1a, 0x53, 0x22, 0x68, 0x11, 0xa0, 0xa1, 0x29, 0x15, 0xd4, 0x7f, 0x04, 0xab,
0x86, 0xd5, 0x67, 0x91, 0x90, 0x43, 0x16, 0x0a, 0x4c, 0x05, 0x67, 0xa9, 0xa0, 0xe8, 0x03, 0xa8, 0x4f, 0x23, 0x21, 0x47, 0x2c, 0x14, 0x98, 0x0a, 0xce, 0x52, 0x41, 0xd1, 0x87, 0xd0, 0xe4, 0x2c,
0x73, 0x16, 0x0a, 0xaf, 0xda, 0xa9, 0x6d, 0x34, 0x7b, 0x6b, 0xdd, 0xab, 0x4a, 0x74, 0x87, 0x2c, 0x14, 0x5e, 0xbd, 0xd7, 0xd8, 0x68, 0x0f, 0xd6, 0xfa, 0xf3, 0x9d, 0xe8, 0x8f, 0x58, 0x88, 0x35,
0xc4, 0x9a, 0xe0, 0xff, 0xea, 0x40, 0x6d, 0xc8, 0x42, 0x84, 0xa0, 0x9e, 0x92, 0x84, 0xda, 0x1c, 0xc1, 0xff, 0xd5, 0x81, 0xc6, 0x88, 0x85, 0x08, 0x41, 0x33, 0x25, 0x09, 0xb5, 0x39, 0x6a, 0x19,
0xf5, 0x1a, 0xfd, 0x0f, 0x16, 0x38, 0x0b, 0x0f, 0x86, 0x36, 0x35, 0x63, 0xa0, 0xfb, 0x00, 0x21, 0xfd, 0x1f, 0x16, 0x38, 0x0b, 0x0f, 0x47, 0x36, 0x35, 0xa3, 0xa0, 0x7b, 0x00, 0x21, 0xe5, 0x31,
0xe5, 0x31, 0xbb, 0x48, 0x68, 0x2a, 0x6d, 0x4a, 0x33, 0x08, 0x7a, 0x0b, 0x16, 0x85, 0x24, 0x32, 0xbb, 0x4a, 0x68, 0x2a, 0x6d, 0x4a, 0x33, 0x08, 0x7a, 0x1b, 0x16, 0x85, 0x24, 0x32, 0x17, 0x5e,
0x17, 0x5e, 0x5d, 0xfb, 0xac, 0xa5, 0xa2, 0x91, 0x30, 0xa4, 0xa1, 0xb7, 0xd0, 0xa9, 0x6e, 0xb8, 0x53, 0xdb, 0xac, 0xa6, 0xa2, 0x91, 0x30, 0xa4, 0xa1, 0xb7, 0xd0, 0xab, 0x6f, 0xb8, 0xd8, 0x28,
0xd8, 0x18, 0x68, 0x1f, 0xee, 0x88, 0x28, 0x0d, 0xe8, 0x33, 0x22, 0x24, 0xa6, 0x9c, 0x65, 0xd2, 0x68, 0x1f, 0xee, 0x88, 0x28, 0x0d, 0xe8, 0x53, 0x22, 0x24, 0xa6, 0x9c, 0x65, 0xd2, 0x5b, 0xec,
0x5b, 0xec, 0x54, 0x37, 0x9a, 0xbd, 0x7b, 0x5d, 0xd3, 0xad, 0x6e, 0xd1, 0xad, 0xee, 0x13, 0xdb, 0xd5, 0x37, 0xda, 0x83, 0xbb, 0x7d, 0x33, 0xad, 0x7e, 0x31, 0xad, 0xfe, 0x13, 0x3b, 0x2d, 0x5c,
0x2d, 0x3c, 0xbf, 0x03, 0x6d, 0xc1, 0x5a, 0xc0, 0x52, 0x99, 0xb1, 0x38, 0xa6, 0xd9, 0x21, 0x49, 0xf5, 0x40, 0x5b, 0xb0, 0x16, 0xb0, 0x54, 0x66, 0x2c, 0x8e, 0x69, 0x76, 0x44, 0x12, 0x2a, 0x38,
0xa8, 0xe0, 0x24, 0xa0, 0xde, 0x92, 0x3e, 0xff, 0x26, 0x17, 0xf2, 0x61, 0xd9, 0xc2, 0xc3, 0x98, 0x09, 0xa8, 0xb7, 0xa4, 0xcf, 0xbf, 0xc9, 0x84, 0x7c, 0x58, 0xb6, 0xf0, 0x28, 0x26, 0x29, 0xf5,
0xa4, 0xd4, 0x73, 0x75, 0x4e, 0x57, 0x30, 0xff, 0x77, 0x07, 0x60, 0x44, 0x38, 0xa6, 0x2f, 0x72, 0x5c, 0x9d, 0xd3, 0x1c, 0xe6, 0xff, 0xee, 0x00, 0x9c, 0x12, 0x8e, 0xe9, 0xf3, 0x9c, 0x0a, 0x89,
0x2a, 0x24, 0x42, 0x50, 0xe3, 0x2c, 0x34, 0x02, 0x0d, 0x2a, 0x58, 0x19, 0xa8, 0x73, 0x45, 0x0b, 0x10, 0x34, 0x38, 0x0b, 0x4d, 0x83, 0x0e, 0x6a, 0x58, 0x29, 0xa8, 0x37, 0xd7, 0x0b, 0xc7, 0x9a,
0xc7, 0xba, 0xe6, 0xd4, 0x48, 0xc8, 0xcf, 0x98, 0x0b, 0xad, 0x94, 0x83, 0xad, 0xa5, 0x70, 0xc9, 0x2a, 0xdd, 0x48, 0xc8, 0xcf, 0x98, 0x0b, 0xdd, 0x29, 0x07, 0x5b, 0x4d, 0xe1, 0x92, 0x8d, 0x54,
0x86, 0xaa, 0x5c, 0xa5, 0x52, 0x0b, 0x5b, 0x4b, 0xf5, 0x41, 0xb2, 0x83, 0xa1, 0x16, 0xa9, 0x81, 0xb9, 0xaa, 0x4b, 0x1d, 0x6c, 0x35, 0x35, 0x07, 0xc9, 0x0e, 0x47, 0xba, 0x49, 0x2d, 0xac, 0x65,
0xf5, 0x1a, 0xb5, 0xc1, 0x3d, 0xc9, 0x58, 0x32, 0x2c, 0xc4, 0x69, 0xe1, 0xd2, 0x56, 0x71, 0xd4, 0xd4, 0x05, 0xf7, 0x2c, 0x63, 0xc9, 0xa8, 0x68, 0x4e, 0x07, 0x97, 0xba, 0x8a, 0xa3, 0xe4, 0xc3,
0xfa, 0x60, 0x68, 0xab, 0xb5, 0x96, 0xee, 0x42, 0x30, 0xa5, 0x89, 0x29, 0x4d, 0x75, 0x41, 0x5b, 0x91, 0xad, 0xd6, 0x6a, 0x7a, 0x0a, 0xc1, 0x94, 0x26, 0xa6, 0x34, 0x35, 0x05, 0xad, 0xe9, 0x7c,
0x3a, 0x1f, 0x2a, 0xa7, 0x2c, 0xf4, 0x1a, 0x06, 0x37, 0x96, 0x1a, 0x45, 0x92, 0xcb, 0x29, 0xcb, 0xa8, 0x9c, 0xb2, 0xd0, 0x6b, 0x19, 0xdc, 0x68, 0x6a, 0x15, 0x49, 0x2e, 0xa7, 0x2c, 0x8b, 0xe4,
0x22, 0x79, 0xe1, 0x81, 0x19, 0xc5, 0x12, 0x50, 0x59, 0x71, 0x22, 0xa7, 0x5e, 0xd3, 0x64, 0xa5, 0x95, 0x07, 0x66, 0x15, 0x4b, 0x40, 0x65, 0xc5, 0x89, 0x9c, 0x7a, 0x6d, 0x93, 0x95, 0x92, 0xf7,
0xd6, 0x7b, 0x2e, 0x2c, 0x4a, 0x92, 0x9d, 0x52, 0xe9, 0x77, 0xc0, 0xdd, 0xe5, 0x51, 0x3f, 0xcb, 0x5c, 0x58, 0x94, 0x24, 0x3b, 0xa7, 0xd2, 0xef, 0x81, 0xbb, 0xcb, 0xa3, 0x61, 0x96, 0xb1, 0x4c,
0x58, 0xa6, 0xba, 0x4c, 0xd5, 0xc2, 0x0e, 0x92, 0x31, 0xfc, 0x21, 0xb8, 0x98, 0x0a, 0x96, 0x67, 0x4d, 0x99, 0x2a, 0xc1, 0x2e, 0x92, 0x51, 0xfc, 0x11, 0xb8, 0x98, 0x0a, 0x96, 0x67, 0x01, 0x55,
0x01, 0x55, 0x27, 0xa5, 0x65, 0x8b, 0xec, 0x95, 0x28, 0x01, 0x5d, 0xff, 0x05, 0x2f, 0x6e, 0x83, 0x27, 0xa5, 0xe5, 0x88, 0xec, 0x95, 0x28, 0x01, 0x5d, 0xff, 0x15, 0x2f, 0x6e, 0x83, 0x96, 0xcb,
0x5e, 0x97, 0xb3, 0x59, 0xbb, 0x9c, 0x4d, 0x9f, 0xc3, 0xdd, 0x22, 0xe2, 0x31, 0x8d, 0x69, 0xa0, 0xdd, 0x6c, 0x5c, 0xef, 0xa6, 0xcf, 0xe1, 0x7f, 0x45, 0xc4, 0x13, 0x1a, 0xd3, 0x40, 0x2d, 0x06,
0x06, 0x03, 0x7d, 0x06, 0x6e, 0x66, 0x41, 0x1d, 0xb9, 0xd9, 0xf3, 0xe6, 0x27, 0xbf, 0xd8, 0x84, 0xfa, 0x1c, 0xdc, 0xcc, 0x82, 0x3a, 0x72, 0x7b, 0xe0, 0x55, 0x37, 0xbf, 0x70, 0xc2, 0x25, 0x13,
0x4b, 0x26, 0x7a, 0x1f, 0x56, 0x62, 0x32, 0xa1, 0xf1, 0x58, 0xe8, 0x40, 0x2c, 0xb3, 0x87, 0xb7, 0x7d, 0x00, 0x2b, 0x31, 0x99, 0xd0, 0x78, 0x2c, 0x74, 0x20, 0x96, 0xd9, 0xc3, 0x3b, 0x1a, 0x3d,
0x34, 0x7a, 0x6c, 0x41, 0xff, 0x07, 0x68, 0x15, 0x9b, 0x4d, 0xa9, 0x6f, 0x76, 0x5a, 0x29, 0x90, 0xb1, 0xa0, 0xff, 0x03, 0x74, 0x0a, 0x67, 0x53, 0xea, 0x9b, 0x9d, 0x56, 0x36, 0xc8, 0x99, 0x6d,
0x33, 0x2b, 0xd0, 0xdf, 0x0e, 0xa0, 0x63, 0x49, 0xe4, 0x71, 0x9e, 0x24, 0x24, 0xbb, 0x28, 0x66, 0xd0, 0x9f, 0x0e, 0xa0, 0x13, 0x49, 0xe4, 0x49, 0x9e, 0x24, 0x24, 0xbb, 0x2a, 0x76, 0xee, 0x4b,
0xee, 0x0b, 0x70, 0xcb, 0xa4, 0xcc, 0x11, 0xef, 0xdd, 0x76, 0x44, 0xa9, 0x02, 0x2e, 0xb7, 0xa0, 0x70, 0xcb, 0xa4, 0xcc, 0x11, 0xef, 0xdf, 0x76, 0x44, 0xd9, 0x05, 0x5c, 0xba, 0xa0, 0x47, 0xd0,
0xc7, 0xd0, 0x94, 0x51, 0x42, 0xc7, 0xe7, 0x51, 0x1a, 0xb2, 0x73, 0x7d, 0xe2, 0x4a, 0xaf, 0x3d, 0x96, 0x51, 0x42, 0xc7, 0x97, 0x51, 0x1a, 0xb2, 0x4b, 0x7d, 0xe2, 0xca, 0xa0, 0x5b, 0x8d, 0x70,
0x1f, 0x61, 0x14, 0x25, 0xf4, 0x3b, 0xcd, 0xc0, 0x20, 0xcb, 0x35, 0xfa, 0x08, 0xea, 0x29, 0x4b, 0x1a, 0x25, 0xf4, 0x3b, 0xcd, 0xc0, 0x20, 0x4b, 0x19, 0x7d, 0x0c, 0xcd, 0x94, 0xa5, 0xa6, 0xeb,
0x8d, 0xea, 0xcd, 0xde, 0xff, 0xe7, 0x77, 0xe9, 0xcf, 0xdd, 0xa0, 0x82, 0x35, 0x09, 0xed, 0xc1, 0xed, 0xc1, 0x5b, 0x55, 0x2f, 0xfd, 0xb9, 0x3b, 0xa8, 0x61, 0x4d, 0xd2, 0x27, 0xb1, 0x71, 0xd9,
0x1d, 0x96, 0xcb, 0xb1, 0x64, 0xe3, 0x52, 0x92, 0xfa, 0xcb, 0x25, 0x19, 0x54, 0x70, 0x8b, 0xe5, 0x8e, 0xe6, 0xcb, 0xdb, 0xa1, 0xee, 0x88, 0x64, 0xe5, 0x46, 0x3c, 0x86, 0x8e, 0xda, 0xda, 0x6b,
0x72, 0xc4, 0xca, 0xc1, 0x78, 0x0a, 0x77, 0x55, 0x0c, 0x35, 0xc0, 0x97, 0x51, 0x16, 0x5e, 0x19, 0xf7, 0x85, 0x57, 0xba, 0x2f, 0x2b, 0x87, 0x42, 0xdf, 0x03, 0x70, 0x59, 0x2e, 0x27, 0x2c, 0x4f,
0x45, 0x1d, 0xfc, 0x34, 0x63, 0x49, 0x01, 0xed, 0x01, 0xb8, 0x2c, 0x97, 0x13, 0x96, 0xa7, 0xa1, 0x43, 0xff, 0xef, 0x3a, 0xac, 0xcd, 0x75, 0xd2, 0x7e, 0x11, 0xbf, 0x00, 0x87, 0x5d, 0xd8, 0x26,
0xff, 0x57, 0x15, 0xd6, 0xae, 0xe8, 0x6a, 0xbf, 0x8f, 0x9f, 0x83, 0xc3, 0xce, 0xac, 0xa4, 0xeb, 0xae, 0x57, 0x23, 0xdf, 0xe0, 0xd0, 0x3f, 0xbe, 0x38, 0xa8, 0x61, 0x87, 0x5d, 0xa0, 0xed, 0xd9,
0xf3, 0xc1, 0x6f, 0xd8, 0xd0, 0x3d, 0x3a, 0x1b, 0x54, 0xb0, 0xc3, 0xce, 0xd0, 0xf6, 0x6c, 0xff, 0x89, 0xb5, 0x07, 0xef, 0xdd, 0x96, 0x96, 0xde, 0x8a, 0x83, 0x9a, 0x1d, 0x69, 0xf7, 0x2b, 0x70,
0x9a, 0xbd, 0x77, 0x6f, 0xcb, 0x4c, 0xcf, 0xc8, 0xa0, 0x62, 0x1b, 0xdc, 0xfe, 0x0a, 0x9c, 0xa3, 0x8e, 0x2f, 0xd0, 0x43, 0x68, 0xab, 0xef, 0xdf, 0x58, 0x92, 0x49, 0x4c, 0x8b, 0xef, 0xf1, 0xdd,
0x33, 0xf4, 0x08, 0x9a, 0xea, 0x6b, 0x38, 0x96, 0x64, 0x12, 0xd3, 0xe2, 0xeb, 0x7c, 0xef, 0xa6, 0x9b, 0xce, 0x3f, 0x55, 0x0c, 0x0c, 0xa2, 0x10, 0x85, 0x2a, 0x2b, 0xb3, 0xd9, 0xf8, 0x7f, 0xd5,
0xf3, 0x47, 0x8a, 0x81, 0x41, 0x14, 0x4b, 0xa1, 0xca, 0xca, 0x6c, 0x36, 0xfe, 0x9f, 0x55, 0x80, 0x01, 0xf6, 0x88, 0x88, 0x02, 0x45, 0x15, 0xe8, 0x3e, 0x74, 0x44, 0x1e, 0x04, 0x54, 0x88, 0x71,
0x3d, 0x22, 0xa2, 0x40, 0x51, 0x05, 0x7a, 0x00, 0x2d, 0x91, 0x07, 0x01, 0x15, 0x62, 0x1c, 0xb0, 0xc0, 0xf2, 0x54, 0xea, 0xc2, 0x9a, 0x78, 0xd9, 0x82, 0xfb, 0x0a, 0x53, 0xa4, 0x33, 0x12, 0xc5,
0x3c, 0x95, 0xba, 0xb0, 0x3a, 0x5e, 0xb6, 0xe0, 0xbe, 0xc2, 0x14, 0xe9, 0x84, 0x44, 0x71, 0x9e, 0x79, 0x46, 0x2d, 0xc9, 0x31, 0x24, 0x0b, 0x1a, 0xd2, 0x03, 0xb5, 0xfd, 0x92, 0xa6, 0xc1, 0xd5,
0x51, 0x4b, 0x72, 0x0c, 0xc9, 0x82, 0x86, 0xf4, 0x50, 0xdd, 0x05, 0x49, 0xd3, 0xe0, 0x62, 0x9c, 0x38, 0x11, 0x63, 0xbe, 0xbd, 0xa5, 0x07, 0xde, 0xc4, 0xcb, 0x16, 0x7d, 0x26, 0x46, 0xdb, 0x5b,
0x88, 0x31, 0xdf, 0xde, 0xd2, 0xed, 0xaf, 0xe3, 0x65, 0x8b, 0x3e, 0x17, 0xc3, 0xed, 0xad, 0x79, 0x55, 0xd6, 0xce, 0xb6, 0x1e, 0xf1, 0x1c, 0x6b, 0x67, 0xfb, 0x05, 0xd6, 0x8e, 0x9e, 0xe4, 0x3c,
0xd6, 0xce, 0xb6, 0x6e, 0xf6, 0x15, 0xd6, 0xce, 0xf6, 0x35, 0xd6, 0x8e, 0x6e, 0xe6, 0x55, 0xd6, 0x6b, 0xc7, 0xff, 0xad, 0x01, 0xad, 0xb2, 0x60, 0xb4, 0x0b, 0x2d, 0xce, 0xc2, 0xf1, 0x79, 0xc6,
0x8e, 0xff, 0x5b, 0x0d, 0x1a, 0x65, 0xc1, 0x68, 0x17, 0x1a, 0x9c, 0x85, 0xe3, 0xd3, 0x8c, 0xe5, 0x72, 0x6e, 0xc7, 0xe3, 0xdf, 0xda, 0x1e, 0xf5, 0xc7, 0xf5, 0xb5, 0x62, 0x1e, 0xd4, 0xb0, 0xcb,
0xdc, 0xb6, 0xc7, 0xbf, 0x55, 0x1e, 0xf5, 0x37, 0xf6, 0xb5, 0x62, 0x0e, 0x2a, 0xd8, 0xe5, 0x76, 0xad, 0xdc, 0xfd, 0xc3, 0x01, 0xb7, 0x30, 0xa0, 0x87, 0xd0, 0xcc, 0xd8, 0x65, 0xd1, 0xe9, 0xf5,
0xdd, 0xfe, 0xc3, 0x01, 0xb7, 0x70, 0xa0, 0x47, 0x50, 0xcf, 0xd8, 0x79, 0xa1, 0xf4, 0xfa, 0xab, 0x57, 0x87, 0xea, 0x63, 0x76, 0x89, 0xb5, 0x4f, 0xf7, 0x9f, 0x3a, 0x34, 0x30, 0xbb, 0x7c, 0xc3,
0x43, 0x75, 0x31, 0x3b, 0xc7, 0x7a, 0x4f, 0xfb, 0x9f, 0x2a, 0xd4, 0x30, 0x3b, 0x7f, 0xc3, 0x7b, 0x9b, 0xfd, 0x9f, 0x6e, 0xdb, 0x06, 0xac, 0x26, 0x54, 0x4c, 0x69, 0x38, 0x56, 0xdd, 0x30, 0xe3,
0xfe, 0x9f, 0xee, 0xde, 0x06, 0xac, 0x26, 0x54, 0x4c, 0x69, 0x38, 0x56, 0x6a, 0x98, 0x76, 0x99, 0x32, 0x83, 0x58, 0x31, 0xf8, 0x88, 0x85, 0x66, 0x60, 0xeb, 0x70, 0x47, 0x32, 0x49, 0xe2, 0x19,
0x46, 0xac, 0x18, 0x7c, 0xc8, 0x42, 0xd3, 0xb0, 0x75, 0xb8, 0x23, 0x99, 0x24, 0xf1, 0x0c, 0xd1, 0xa2, 0x99, 0x45, 0x47, 0xc3, 0x25, 0x6f, 0x0b, 0x16, 0xd4, 0x2e, 0x09, 0x7b, 0x9b, 0x5e, 0x48,
0xf4, 0xa2, 0xa5, 0xe1, 0x92, 0xb7, 0x05, 0x0b, 0x6a, 0x96, 0x84, 0xbd, 0x50, 0xd7, 0x12, 0xb9, 0xe4, 0x7a, 0x9b, 0xb0, 0x21, 0xee, 0x2d, 0xc1, 0x82, 0x5e, 0xd3, 0x8f, 0x76, 0x01, 0xae, 0xd3,
0x9c, 0x26, 0x6c, 0x88, 0x7b, 0x4b, 0xb0, 0xa0, 0xc7, 0xf4, 0xc3, 0x5d, 0x80, 0xcb, 0x34, 0x51, 0x44, 0x6d, 0x58, 0x3a, 0x1d, 0x1e, 0x8d, 0x4f, 0x86, 0xfb, 0xab, 0x35, 0xa5, 0x1c, 0x1f, 0x0d,
0x13, 0x96, 0x46, 0xfd, 0xc3, 0xf1, 0x71, 0x7f, 0x7f, 0xb5, 0xa2, 0x8c, 0xa3, 0xc3, 0xfe, 0xf8, 0xc7, 0xcf, 0x0e, 0x8f, 0x56, 0xeb, 0x85, 0x45, 0x29, 0x0e, 0x5a, 0x06, 0x57, 0x59, 0x0e, 0x8e,
0xf9, 0xc1, 0xe1, 0x6a, 0xb5, 0xf0, 0x28, 0xc3, 0x41, 0xcb, 0xe0, 0x2a, 0xcf, 0xe0, 0xe8, 0x1b, 0xbf, 0xc1, 0xab, 0x8d, 0xc1, 0x2f, 0x0d, 0x68, 0xec, 0xf2, 0x08, 0x7d, 0x0f, 0xed, 0x99, 0xcb,
0xbc, 0x5a, 0xeb, 0xfd, 0x52, 0x83, 0xda, 0x2e, 0x8f, 0xd0, 0xf7, 0xd0, 0x9c, 0xb9, 0x5c, 0xc8, 0x85, 0xfc, 0x97, 0xde, 0x3c, 0xfd, 0xd1, 0xeb, 0xde, 0x7f, 0x8d, 0xdb, 0xe9, 0xd7, 0xd0, 0x63,
0x7f, 0xe9, 0xcd, 0xd3, 0x9f, 0xc0, 0xf6, 0x83, 0xd7, 0xb8, 0x9d, 0x7e, 0x05, 0x7d, 0x09, 0x4b, 0x58, 0x2a, 0xde, 0x51, 0x37, 0x7f, 0x9c, 0xba, 0xef, 0x54, 0xe1, 0x99, 0x97, 0x99, 0x5f, 0x43,
0xc5, 0xab, 0xea, 0xe6, 0x4f, 0x55, 0xfb, 0xed, 0x79, 0x78, 0xe6, 0x9d, 0xe6, 0x57, 0x50, 0x1f, 0x43, 0x70, 0x8b, 0x77, 0xd3, 0x6d, 0x11, 0x7a, 0x55, 0xb8, 0xfa, 0xd0, 0xf2, 0x6b, 0xe8, 0x47,
0xdc, 0xe2, 0x15, 0x75, 0x5b, 0x84, 0xce, 0x3c, 0x3c, 0xff, 0xec, 0xf2, 0x2b, 0xe8, 0x47, 0x68, 0x68, 0x9d, 0xd0, 0xf8, 0x6c, 0x5f, 0xbd, 0x06, 0xd1, 0x27, 0xa5, 0x83, 0x7d, 0x44, 0xce, 0x3e,
0x1c, 0xd3, 0xf8, 0x64, 0x5f, 0xbd, 0x0d, 0xd1, 0xc7, 0xe5, 0x06, 0xfb, 0xa4, 0x9c, 0x7d, 0x38, 0x15, 0x4b, 0x5a, 0x51, 0xe9, 0xa7, 0xaf, 0xc9, 0x9e, 0xa9, 0xb9, 0x71, 0x4a, 0x38, 0x7a, 0x71,
0x96, 0xb4, 0xa2, 0xd2, 0x4f, 0x5e, 0x93, 0x3d, 0x53, 0x73, 0x6d, 0x44, 0x38, 0xba, 0x3e, 0x54, 0xa9, 0xca, 0x67, 0x4a, 0xd7, 0xab, 0xc6, 0x3c, 0x25, 0x7c, 0xf8, 0x13, 0x4d, 0xa5, 0x5f, 0xdb,
0xe5, 0xa3, 0xa5, 0xed, 0xcd, 0xc7, 0x1c, 0x11, 0xde, 0xff, 0x89, 0xa6, 0xd2, 0xaf, 0x6c, 0x55, 0xaa, 0x4f, 0x16, 0xf5, 0x6b, 0xea, 0xb3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x9d, 0x3d,
0x27, 0x8b, 0xfa, 0x6d, 0xf5, 0xe9, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x43, 0x1e, 0x96, 0xb2, 0xa9, 0x2d, 0x0b, 0x00, 0x00,
0x3b, 0x0b, 0x00, 0x00,
} }

View File

@ -10,6 +10,7 @@ import (
const ( const (
KubernetesDeployments = "deployments" KubernetesDeployments = "deployments"
KubernetesNamespaces = "namespaces"
KubernetesPods = "pods" KubernetesPods = "pods"
) )
@ -58,6 +59,8 @@ func CanonicalKubernetesNameFromFriendlyName(friendlyName string) (string, error
switch friendlyName { switch friendlyName {
case "deploy", "deployment", "deployments": case "deploy", "deployment", "deployments":
return KubernetesDeployments, nil return KubernetesDeployments, nil
case "ns", "namespace", "namespaces":
return KubernetesNamespaces, nil
case "po", "pod", "pods": case "po", "pod", "pods":
return KubernetesPods, nil return KubernetesPods, nil
} }

View File

@ -80,8 +80,8 @@ message StatSummaryRequest {
oneof outbound { oneof outbound {
Empty none = 3; Empty none = 3;
Resource out_to_resource = 4; Resource to_resource = 4;
Resource out_from_resource = 5; Resource from_resource = 5;
} }
} }

View File

@ -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) { func (h *handler) handleApiStat(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
requestParams := util.StatSummaryRequestParams{ requestParams := util.StatSummaryRequestParams{
TimeWindow: req.FormValue("window"), TimeWindow: req.FormValue("window"),
ResourceName: req.FormValue("resource_name"), ResourceName: req.FormValue("resource_name"),
ResourceType: req.FormValue("resource_type"), ResourceType: req.FormValue("resource_type"),
Namespace: req.FormValue("namespace"), Namespace: req.FormValue("namespace"),
OutToName: req.FormValue("out_to_name"), ToName: req.FormValue("to_name"),
OutToType: req.FormValue("out_to_type"), ToType: req.FormValue("to_type"),
OutToNamespace: req.FormValue("out_to_namespace"), ToNamespace: req.FormValue("to_namespace"),
OutFromName: req.FormValue("out_from_name"), FromName: req.FormValue("from_name"),
OutFromType: req.FormValue("out_from_type"), FromType: req.FormValue("from_type"),
OutFromNamespace: req.FormValue("out_from_namespace"), FromNamespace: req.FormValue("from_namespace"),
} }
// default to returning deployment stats // default to returning deployment stats