Skip to content

Commit

Permalink
Start implementing conduit stat summary endpoint (#671)
Browse files Browse the repository at this point in the history
Start implementing new conduit stat summary endpoint. 
Changes the public-api to call prometheus directly instead of the
telemetry service. Wired through to `api/stat` on the web server,
as well as `conduit statsummary` on the CLI. Works for deployments only.

Current implementation just retrieves requests and mesh/total pod count 
(so latency stats are always 0). 

Uses API defined in #663
Example queries the stat endpoint will eventually satisfy in #627

This branch includes commits from @klingerf 

* run ./bin/dep ensure
* run ./bin/update-go-deps-shas
  • Loading branch information
rmars authored Apr 6, 2018
1 parent 4fb9877 commit 2f5b5ea
Show file tree
Hide file tree
Showing 18 changed files with 862 additions and 138 deletions.
28 changes: 26 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/Dockerfile-bin
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## compile binaries
FROM gcr.io/runconduit/go-deps:7433a12f as golang
FROM gcr.io/runconduit/go-deps:2bfca396 as golang
ARG CONDUIT_VERSION
WORKDIR /go/src/github.com/runconduit/conduit
COPY cli cli
Expand Down
227 changes: 227 additions & 0 deletions cli/cmd/stat_summary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
package cmd

import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"time"

"github.com/prometheus/common/log"
"github.com/runconduit/conduit/controller/api/util"
pb "github.com/runconduit/conduit/controller/gen/public"
"github.com/spf13/cobra"
)

var namespace, resourceType, resourceName string
var outToNamespace, outToType, outToName string
var outFromNamespace, outFromType, outFromName string

var statSummaryCommand = &cobra.Command{
Use: "statsummary [flags] RESOURCETYPE [RESOURCENAME]",
Short: "Display traffic stats about one or many resources",
Long: `Display traffic stats about one or many resources.
Valid resource types include:
* deployment
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`,
Example: ` conduit statsummary deployments -a test
conduit statsummary deployments all -a test
conduit statsummary deployments hello1 -a test `,
Args: cobra.RangeArgs(1, 2),
ValidArgs: []string{"deployment"},
RunE: func(cmd *cobra.Command, args []string) error {
switch len(args) {
case 1:
resourceType = args[0]
case 2:
resourceType = args[0]
resourceName = args[1]
default:
return errors.New("please specify one resource only")
}

client, err := newPublicAPIClient()
if err != nil {
return fmt.Errorf("error creating api client while making stats request: %v", err)
}

output, err := requestStatSummaryFromAPI(client)
if err != nil {
return err
}

_, err = fmt.Print(output)

return err
},
}

func init() {
RootCmd.AddCommand(statSummaryCommand)
addControlPlaneNetworkingArgs(statSummaryCommand)
// TODO: the -n flag is taken up by conduit-namespace :( we should move it to something else so this can have -n
statSummaryCommand.PersistentFlags().StringVarP(&namespace, "namespace", "a", "default", "namespace of the specified resource")
statSummaryCommand.PersistentFlags().StringVarP(&timeWindow, "time-window", "t", "1m", "Stat window. One of: '10s', '1m', '10m', '1h'.")

statSummaryCommand.PersistentFlags().StringVarP(&outToName, "out-to", "", "", "If present, restricts outbound stats to the specified resource name")
statSummaryCommand.PersistentFlags().StringVarP(&outToNamespace, "out-to-namespace", "", "", "Sets the namespace used to lookup the '--out-to' resource. By default the current '--namespace' is used")
statSummaryCommand.PersistentFlags().StringVarP(&outToType, "out-to-resource", "", "", "If present, restricts outbound stats to the specified resource type")

statSummaryCommand.PersistentFlags().StringVarP(&outFromName, "out-from", "", "", "If present, restricts outbound stats to the specified resource name")
statSummaryCommand.PersistentFlags().StringVarP(&outFromNamespace, "out-from-namespace", "", "", "Sets the namespace used to lookup the '--out-from' resource. By default the current '--namespace' is used")
statSummaryCommand.PersistentFlags().StringVarP(&outFromType, "out-from-resource", "", "", "If present, restricts outbound stats to the specified resource type")
}

func requestStatSummaryFromAPI(client pb.ApiClient) (string, error) {
req, err := buildStatSummaryRequest()

if err != nil {
return "", fmt.Errorf("error creating metrics request while making stats request: %v", err)
}

resp, err := client.StatSummary(context.Background(), req)
if err != nil {
return "", fmt.Errorf("error calling stat with request: %v", err)
}

return renderStatSummary(resp), nil
}

func renderStatSummary(resp *pb.StatSummaryResponse) string {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 0, padding, ' ', tabwriter.AlignRight)

writeStatTableToBuffer(resp, w)
w.Flush()

// strip left padding on the first column
out := string(buffer.Bytes()[padding:])
out = strings.Replace(out, "\n"+strings.Repeat(" ", padding), "\n", -1)

return out
}

type summaryRow struct {
meshed string
requestRate float64
successRate float64
latencyP50 int64
latencyP99 int64
}

func writeStatTableToBuffer(resp *pb.StatSummaryResponse, w *tabwriter.Writer) {
nameHeader := "NAME"
maxNameLength := len(nameHeader)

stats := make(map[string]*summaryRow)

for _, statTable := range resp.GetOk().StatTables {
table := statTable.GetPodGroup()
for _, r := range table.Rows {
name := r.Resource.Name
if name == "" {
continue
}

if len(name) > maxNameLength {
maxNameLength = len(name)
}

stats[name] = &summaryRow{
meshed: fmt.Sprintf("%d/%d", r.MeshedPodCount, r.TotalPodCount),
requestRate: getRequestRate(*r),
successRate: getSuccessRate(*r),
}
}
}

fmt.Fprintln(w, strings.Join([]string{
nameHeader + strings.Repeat(" ", maxNameLength-len(nameHeader)),
"MESHED",
"IN_RPS",
"IN_SUCCESS",
"IN_LATENCY_P50",
"IN_LATENCY_P99\t", // trailing \t is required to format last column
}, "\t"))

sortedNames := sortStatSummaryKeys(stats)
for _, name := range sortedNames {
fmt.Fprintf(
w,
"%s\t%s\t%.1frps\t%.2f%%\t%dms\t%dms\t\n",
name+strings.Repeat(" ", maxNameLength-len(name)),
stats[name].meshed,
stats[name].requestRate,
stats[name].successRate*100,
stats[name].latencyP50,
stats[name].latencyP99,
)
}
}

func buildStatSummaryRequest() (*pb.StatSummaryRequest, error) {
requestParams := util.StatSummaryRequestParams{
TimeWindow: timeWindow,
ResourceName: resourceName,
ResourceType: resourceType,
Namespace: namespace,
OutToName: outToName,
OutToType: outToType,
OutToNamespace: outToNamespace,
OutFromName: outFromName,
OutFromType: outFromType,
OutFromNamespace: outFromNamespace,
}

return util.BuildStatSummaryRequest(requestParams)
}

func getRequestRate(r pb.StatTable_PodGroup_Row) float64 {
if r.Stats == nil {
return 0.0
}
success := r.Stats.SuccessCount
failure := r.Stats.FailureCount
window, err := util.GetWindowString(r.TimeWindow)
if err != nil {
log.Error(err.Error())
return 0.0
}

windowLength, err := time.ParseDuration(window)
if err != nil {
log.Error(err.Error())
return 0.0
}
return float64(success+failure) / windowLength.Seconds()
}

func getSuccessRate(r pb.StatTable_PodGroup_Row) float64 {
if r.Stats == nil {
return 0.0
}

success := r.Stats.SuccessCount
failure := r.Stats.FailureCount

if success+failure == 0 {
return 0.0
}
return float64(success) / float64(success+failure)
}

func sortStatSummaryKeys(stats map[string]*summaryRow) []string {
var sortedKeys []string
for key := range stats {
sortedKeys = append(sortedKeys, key)
}
sort.Strings(sortedKeys)
return sortedKeys
}
3 changes: 2 additions & 1 deletion cli/cmd/testdata/install_default.golden
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: conduit-controller
rules:
- apiGroups: ["extensions"]
- apiGroups: ["extensions", "apps"]
resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"]
- apiGroups: [""]
Expand Down Expand Up @@ -142,6 +142,7 @@ spec:
containers:
- args:
- public-api
- -prometheus-url=http://prometheus.conduit.svc.cluster.local:9090
- -controller-namespace=conduit
- -log-level=info
- -logtostderr=true
Expand Down
3 changes: 2 additions & 1 deletion cli/cmd/testdata/install_output.golden
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: conduit-controller
rules:
- apiGroups: ["extensions"]
- apiGroups: ["extensions", "apps"]
resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"]
- apiGroups: [""]
Expand Down Expand Up @@ -143,6 +143,7 @@ spec:
containers:
- args:
- public-api
- -prometheus-url=http://prometheus.Namespace.svc.cluster.local:9090
- -controller-namespace=Namespace
- -log-level=ControllerLogLevel
- -logtostderr=true
Expand Down
3 changes: 2 additions & 1 deletion cli/install/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: conduit-controller
rules:
- apiGroups: ["extensions"]
- apiGroups: ["extensions", "apps"]
resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"]
- apiGroups: [""]
Expand Down Expand Up @@ -148,6 +148,7 @@ spec:
imagePullPolicy: {{.ImagePullPolicy}}
args:
- "public-api"
- "-prometheus-url=http://prometheus.{{.Namespace}}.svc.cluster.local:9090"
- "-controller-namespace={{.Namespace}}"
- "-log-level={{.ControllerLogLevel}}"
- "-logtostderr=true"
Expand Down
2 changes: 1 addition & 1 deletion controller/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## compile controller services
FROM gcr.io/runconduit/go-deps:7433a12f as golang
FROM gcr.io/runconduit/go-deps:2bfca396 as golang
ARG CONDUIT_VERSION
WORKDIR /go/src/github.com/runconduit/conduit
COPY controller/gen controller/gen
Expand Down
6 changes: 5 additions & 1 deletion controller/api/public/grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ var (
)

func newGrpcServer(telemetryClient telemPb.TelemetryClient, tapClient tapPb.TapClient, controllerNamespace string) *grpcServer {
return &grpcServer{telemetryClient: telemetryClient, tapClient: tapClient, controllerNamespace: controllerNamespace}
return &grpcServer{
telemetryClient: telemetryClient,
tapClient: tapClient,
controllerNamespace: controllerNamespace,
}
}

func (s *grpcServer) Stat(ctx context.Context, req *pb.MetricRequest) (*pb.MetricResponse, error) {
Expand Down
Loading

0 comments on commit 2f5b5ea

Please sign in to comment.