package tap import ( "context" "testing" "time" public "github.com/runconduit/conduit/controller/gen/public" "github.com/runconduit/conduit/controller/k8s" ) type tapExpected struct { msg string k8sRes []string req public.TapByResourceRequest eofOk bool } func TestTapByResource(t *testing.T) { t.Run("Returns expected response", func(t *testing.T) { expectations := []tapExpected{ tapExpected{ msg: "rpc error: code = InvalidArgument desc = TapByResource received nil target ResourceSelection: {Target: Match: MaxRps:0}", k8sRes: []string{}, req: public.TapByResourceRequest{}, }, tapExpected{ msg: "rpc error: code = Unimplemented desc = unexpected match specified: any:<> ", k8sRes: []string{` apiVersion: v1 kind: Pod metadata: name: emojivoto-meshed namespace: emojivoto labels: app: emoji-svc annotations: conduit.io/proxy-version: testinjectversion status: phase: Running `, }, req: public.TapByResourceRequest{ Target: &public.ResourceSelection{ Resource: &public.Resource{ Namespace: "emojivoto", Type: "pods", Name: "emojivoto-meshed", }, }, Match: &public.TapByResourceRequest_Match{ Match: &public.TapByResourceRequest_Match_Any{ Any: &public.TapByResourceRequest_Match_Seq{}, }, }, }, }, tapExpected{ msg: "rpc error: code = Unimplemented desc = unimplemented resource type: bad-type", k8sRes: []string{}, req: public.TapByResourceRequest{ Target: &public.ResourceSelection{ Resource: &public.Resource{ Namespace: "emojivoto", Type: "bad-type", Name: "emojivoto-meshed-not-found", }, }, }, }, tapExpected{ msg: "rpc error: code = NotFound desc = pod \"emojivoto-meshed-not-found\" not found", k8sRes: []string{` apiVersion: v1 kind: Pod metadata: name: emojivoto-meshed namespace: emojivoto labels: app: emoji-svc annotations: conduit.io/proxy-version: testinjectversion status: phase: Running `, }, req: public.TapByResourceRequest{ Target: &public.ResourceSelection{ Resource: &public.Resource{ Namespace: "emojivoto", Type: "pods", Name: "emojivoto-meshed-not-found", }, }, }, }, tapExpected{ msg: "rpc error: code = NotFound desc = no pods found for ResourceSelection: {Resource:namespace:\"emojivoto\" type:\"pods\" name:\"emojivoto-meshed\" LabelSelector:}", k8sRes: []string{` apiVersion: v1 kind: Pod metadata: name: emojivoto-meshed namespace: emojivoto labels: app: emoji-svc annotations: conduit.io/proxy-version: testinjectversion status: phase: Finished `, }, req: public.TapByResourceRequest{ Target: &public.ResourceSelection{ Resource: &public.Resource{ Namespace: "emojivoto", Type: "pods", Name: "emojivoto-meshed", }, }, }, }, tapExpected{ // indicates we will accept EOF, in addition to the deadline exceeded message eofOk: true, // success, underlying tap events tested in http_server_test.go msg: "rpc error: code = DeadlineExceeded desc = context deadline exceeded", k8sRes: []string{` apiVersion: v1 kind: Pod metadata: name: emojivoto-meshed namespace: emojivoto labels: app: emoji-svc annotations: conduit.io/proxy-version: testinjectversion status: phase: Running `, }, req: public.TapByResourceRequest{ Target: &public.ResourceSelection{ Resource: &public.Resource{ Namespace: "emojivoto", Type: "pods", Name: "emojivoto-meshed", }, }, Match: &public.TapByResourceRequest_Match{ Match: &public.TapByResourceRequest_Match_All{ All: &public.TapByResourceRequest_Match_Seq{}, }, }, }, }, } for _, exp := range expectations { k8sAPI, err := k8s.NewFakeAPI(exp.k8sRes...) if err != nil { t.Fatalf("NewFakeAPI returned an error: %s", err) } server, listener, err := NewServer("localhost:0", 0, k8sAPI) if err != nil { t.Fatalf("NewServer error: %s", err) } go func() { server.Serve(listener) }() defer server.GracefulStop() err = k8sAPI.Sync() if err != nil { t.Fatalf("k8sAPI.Sync() returned an error: %s", err) } client, conn, err := NewClient(listener.Addr().String()) if err != nil { t.Fatalf("NewClient error: %v", err) } defer conn.Close() // TODO: mock out the underlying grpc tap events, rather than waiting an // arbitrary time for request to timeout. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() tapClient, err := client.TapByResource(ctx, &exp.req) if err != nil { t.Fatalf("TapByResource failed: %v", err) } _, err = tapClient.Recv() if err.Error() != exp.msg && (!exp.eofOk || err.Error() != "EOF") { t.Fatalf("Expected error to be [%s], but was [%s]. eofOk: %v", exp.msg, err, exp.eofOk) } } }) }