linkerd2/controller/api/public/stat_summary_test.go

220 lines
5.5 KiB
Go

package public
import (
"context"
"errors"
"reflect"
"testing"
"time"
"github.com/prometheus/common/model"
tap "github.com/runconduit/conduit/controller/gen/controller/tap"
pb "github.com/runconduit/conduit/controller/gen/public"
"github.com/runconduit/conduit/pkg/k8s"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/cache"
)
type statSumExpected struct {
err error
k8sRes []string
promRes model.Value
req pb.StatSummaryRequest
res pb.StatSummaryResponse
}
func TestStatSummary(t *testing.T) {
t.Run("Successfully performs a query based on resource type", func(t *testing.T) {
expectations := []statSumExpected{
statSumExpected{
err: nil,
k8sRes: []string{`
apiVersion: apps/v1
kind: Deployment
metadata:
name: emoji
namespace: emojivoto
spec:
selector:
matchLabels:
app: emoji-svc
strategy: {}
template:
spec:
containers:
- image: buoyantio/emojivoto-emoji-svc:v3
`, `
apiVersion: v1
kind: Pod
metadata:
name: emojivoto-meshed
namespace: emojivoto
labels:
app: emoji-svc
annotations:
conduit.io/proxy-version: testinjectversion
`, `
apiVersion: v1
kind: Pod
metadata:
name: emojivoto-not-meshed
namespace: emojivoto
labels:
app: emoji-svc
`,
},
promRes: model.Vector{
&model.Sample{
Metric: model.Metric{"deployment": "emoji", "classification": "success"},
Value: 123,
Timestamp: 456,
},
},
req: pb.StatSummaryRequest{
Selector: &pb.ResourceSelection{
Resource: &pb.Resource{
Namespace: "emojivoto",
Type: k8s.KubernetesDeployments,
},
},
TimeWindow: pb.TimeWindow_ONE_MIN,
},
res: pb.StatSummaryResponse{
Response: &pb.StatSummaryResponse_Ok_{ // https://github.com/golang/protobuf/issues/205
Ok: &pb.StatSummaryResponse_Ok{
StatTables: []*pb.StatTable{
&pb.StatTable{
Table: &pb.StatTable_PodGroup_{
PodGroup: &pb.StatTable_PodGroup{
Rows: []*pb.StatTable_PodGroup_Row{
&pb.StatTable_PodGroup_Row{
Resource: &pb.Resource{
Namespace: "emojivoto",
Type: "deployments",
Name: "emoji",
},
Stats: &pb.BasicStats{
SuccessCount: 123,
FailureCount: 0,
LatencyMsP50: 123,
LatencyMsP95: 123,
LatencyMsP99: 123,
},
TimeWindow: pb.TimeWindow_ONE_MIN,
MeshedPodCount: 1,
TotalPodCount: 2,
},
},
},
},
},
},
},
},
},
},
}
for _, exp := range expectations {
k8sObjs := []runtime.Object{}
for _, res := range exp.k8sRes {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(res), nil, nil)
if err != nil {
t.Fatalf("could not decode yml: %s", err)
}
k8sObjs = append(k8sObjs, obj)
}
clientSet := fake.NewSimpleClientset(k8sObjs...)
sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute)
deployInformer := sharedInformers.Apps().V1().Deployments()
podInformer := sharedInformers.Core().V1().Pods()
fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes},
&mockTelemetry{},
tap.NewTapClient(nil),
deployInformer.Lister(),
podInformer.Lister(),
"conduit",
)
stopCh := make(chan struct{})
sharedInformers.Start(stopCh)
if !cache.WaitForCacheSync(stopCh, deployInformer.Informer().HasSynced, podInformer.Informer().HasSynced) {
t.Fatalf("timed out wait for caches to sync")
}
rsp, err := fakeGrpcServer.StatSummary(context.TODO(), &exp.req)
if err != exp.err {
t.Fatalf("Expected error: %s, Got: %s", exp.err, err)
}
if !reflect.DeepEqual(exp.res, *rsp) {
t.Fatalf("Expected: %+v, Got: %+v", &exp.res, rsp)
}
}
})
t.Run("Given an invalid resource type, returns error", func(t *testing.T) {
expectations := []statSumExpected{
statSumExpected{
err: errors.New("Unimplemented resource type: badtype"),
req: pb.StatSummaryRequest{
Selector: &pb.ResourceSelection{
Resource: &pb.Resource{
Type: "badtype",
},
},
},
},
statSumExpected{
err: errors.New("Unimplemented resource type: deployment"),
req: pb.StatSummaryRequest{
Selector: &pb.ResourceSelection{
Resource: &pb.Resource{
Type: "deployment",
},
},
},
},
statSumExpected{
err: errors.New("Unimplemented resource type: pod"),
req: pb.StatSummaryRequest{
Selector: &pb.ResourceSelection{
Resource: &pb.Resource{
Type: "pod",
},
},
},
},
}
for _, exp := range expectations {
clientSet := fake.NewSimpleClientset()
sharedInformers := informers.NewSharedInformerFactory(clientSet, 10*time.Minute)
fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes},
&mockTelemetry{},
tap.NewTapClient(nil),
sharedInformers.Apps().V1().Deployments().Lister(),
sharedInformers.Core().V1().Pods().Lister(),
"conduit",
)
_, err := fakeGrpcServer.StatSummary(context.TODO(), &exp.req)
if err != nil || exp.err != nil {
if (err == nil && exp.err != nil) ||
(err != nil && exp.err == nil) ||
(err.Error() != exp.err.Error()) {
t.Fatalf("Unexpected error (Expected: %s, Got: %s)", exp.err, err)
}
}
}
})
}