mirror of https://github.com/linkerd/linkerd2.git
220 lines
5.5 KiB
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)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|