Skip to content

Commit

Permalink
feat: replace buf dependency with grpcurl
Browse files Browse the repository at this point in the history
  • Loading branch information
shifty11 committed Dec 21, 2023
1 parent ec6ab04 commit b0c8ab6
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 34 deletions.
223 changes: 192 additions & 31 deletions tools/kystrap/cmd/test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/KYVENetwork/kyvejs/tools/kystrap/types"
"github.com/fullstorydev/grpcurl"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/protobuf/reflect/protoreflect"
"os/exec"
"os"
"strings"
"time"
)

func findDescriptorMethod(name string) protoreflect.MethodDescriptor {
if name == "" {
return nil
}
lower := strings.ToLower(name)
for _, method := range types.Rdk.MethodList() {
if strings.ToLower(string(method.Name())) == lower || strings.ToLower(string(method.FullName())) == lower {
Expand All @@ -28,16 +37,17 @@ func getPosition(value string) (int, error) {
if method == nil {
return 0, errors.New(fmt.Sprintf("invalid value %s", value))
}
for i := 0; i < types.Rdk.Methods.Len(); i++ {
if types.Rdk.Methods.Get(i).FullName() == method.FullName() {
position = i
}
}
}
return position, nil
}

func promptMethod(defaultVal string, skipPrompt bool) (protoreflect.MethodDescriptor, error) {
position, err := getPosition(defaultVal)
if err != nil {
return nil, err
}
if skipPrompt {
if skipPrompt || defaultVal != "" {
if defaultVal == "" {
return nil, errors.New("no method specified")
}
Expand All @@ -48,6 +58,10 @@ func promptMethod(defaultVal string, skipPrompt bool) (protoreflect.MethodDescri
return method, nil
}

position, err := getPosition(defaultVal)
if err != nil {
return nil, err
}
prompt := promptui.Select{
Label: "Which method do you want to test?",
Items: types.Rdk.MethodNames(),
Expand All @@ -58,49 +72,196 @@ func promptMethod(defaultVal string, skipPrompt bool) (protoreflect.MethodDescri
return findDescriptorMethod(result), err
}

func performBufCurl(method protoreflect.MethodDescriptor) (string, error) {
host, port := "127.0.0.1", "50051"
bufCmd := []string{
"curl",
"--protocol",
"grpc",
"--schema",
"protobuf.descriptor.bin",
"--http2-prior-knowledge",
fmt.Sprintf("http://%s:%s/%s", host, port, method.Name()),
}
out, err := exec.Command("buf", bufCmd...).Output()
func promptInput(field protoreflect.FieldDescriptor, skipPrompt bool) (string, error) {
if skipPrompt {
return "", nil
}
prompt := promptui.Prompt{
Label: field.Name(),
}
result, err := prompt.Run()
return result, err
}

type action string

const (
actionRetry action = "Retry"
actionTestAnother action = "Test another method"
actionExit action = "Exit"
)

func promptAction(isYesNo bool) (action, error) {
if isYesNo {
prompt := promptui.Prompt{
Label: "Continue",
IsConfirm: true,
Default: "y",
}
result, err := prompt.Run()
if err != nil {
return actionExit, err
}
if strings.ToLower(result) == "y" || result == "" {
return actionTestAnother, nil
}
return actionExit, nil
}
prompt := promptui.Select{
Label: "What now?",
Items: []action{actionRetry, actionTestAnother, actionExit},
}
_, result, err := prompt.Run()
if err != nil {
return actionExit, err
}
return action(result), nil
}

func dial() (*grpc.ClientConn, error) {
dialTime := 10 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), dialTime)
defer cancel()
var opts []grpc.DialOption
var creds credentials.TransportCredentials

grpcurlUA := "kystrap"
opts = append(opts, grpc.WithUserAgent(grpcurlUA))

network := "tcp"

target := "localhost:50051"

cc, err := grpcurl.BlockingDial(ctx, network, target, creds, opts...)
if err != nil {
return nil, err
}
return cc, nil
}

func performMethodCall(cmd *cobra.Command, method protoreflect.MethodDescriptor, data string) (bool, error) {
cc, err := dial()
if err != nil {
return false, err
}
defer cc.Close()

dialTime := 10 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), dialTime)
defer cancel()

//in := os.Stdin
in := strings.NewReader(data)

options := grpcurl.FormatOptions{
EmitJSONDefaultFields: true,
AllowUnknownFields: true,
}

rf, formatter, err := grpcurl.RequestParserAndFormatter(grpcurl.FormatJSON, types.Rdk.DescriptorSource, in, options)
if err != nil {
return false, err
}

h := &grpcurl.DefaultEventHandler{
Out: os.Stdout,
Formatter: formatter,
}

err = grpcurl.InvokeRPC(ctx, types.Rdk.DescriptorSource, cc, string(method.FullName()), nil, h, rf.Next)
if err != nil {
return false, err
}
if h.Status.Err() != nil {
cmd.PrintErrln(h.Status.Message())
}
return h.Status.Err() == nil, nil
}

func runTestIntegration(
cmd *cobra.Command,
defaultMethod string,
data string,
skipPromptMethod bool,
skipPromptInput bool,
) (protoreflect.MethodDescriptor, bool, error) {
method, err := promptMethod(defaultMethod, skipPromptMethod)
if err != nil {
//var exitError *exec.ExitError
//if errors.As(err, &exitError) {
// return "", errors.New(string(exitError.Stderr))
//}
return "", err
return nil, false, err
}

if data == "" {
fields := method.Input().Fields()
for i := 0; i < fields.Len(); i++ {
field := fields.Get(i)
input, err := promptInput(field, skipPromptInput)
if err != nil {
return nil, false, err
}

// separate fields with comma
if data != "" {
data += ", "
}
data += fmt.Sprintf(`"%s": "%s"`, field.Name(), input)
}
data = fmt.Sprintf(`{%s}`, data)

if !json.Valid([]byte(data)) {
cmd.PrintErrln("invalid json")
return nil, false, nil
}
}
return string(out), nil

success, err := performMethodCall(cmd, method, data)
return method, success, err
}

func CmdTestIntegration() *cobra.Command {
const flagMethod = "method"
const flagData = "data"

cmd := &cobra.Command{
Use: "test",
Short: "Test a runtime integration",
RunE: func(cmd *cobra.Command, args []string) error {
defaultMethod, _ := cmd.Flags().GetString(flagMethod)
method, err := promptMethod(defaultMethod, cmd.Flags().Changed(yesFlag))
defaultData, _ := cmd.Flags().GetString(flagData)
skip := cmd.Flags().Changed(yesFlag)
method, success, err := runTestIntegration(cmd, defaultMethod, defaultData, skip, skip)
if err != nil {
return err
}
result, err := performBufCurl(method)
if err != nil {
return err

if cmd.Flags().Changed(yesFlag) {
return nil
}

for {
cmd.Println()
actionResult, err := promptAction(success)
if err != nil {
return err
}
switch actionResult {
case actionRetry:
method, success, err = runTestIntegration(cmd, string(method.Name()), "", true, false)
if err != nil {
return err
}
case actionTestAnother:
method, success, err = runTestIntegration(cmd, "", "", false, false)
if err != nil {
return err
}
case actionExit:
return nil
}
}
fmt.Println(result)
return nil
},
}
cmd.Flags().StringP(flagMethod, "m", "", "method that you want to test")
cmd.Flags().StringP(flagMethod, "m", "", "gRPC method that you want to test")
cmd.Flags().StringP(flagData, "d", "", "data that you want to send with the gRPC method call")
return cmd
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
)

const (
ServiceName = "kyverdk.runtime.v1.RuntimeService"
ProtobufDescriptorFile = "protobuf.descriptor.bin"
)

type RdkDescriptor struct {
DescriptorSource grpcurl.DescriptorSource
ServiceDescriptor desc.Descriptor
Expand All @@ -32,7 +37,7 @@ func (r *RdkDescriptor) MethodNames() []string {
}

func parseProtobufDescriptor() error {
sets, err := grpcurl.DescriptorSourceFromProtoSets("protobuf.descriptor.bin")
sets, err := grpcurl.DescriptorSourceFromProtoSets(ProtobufDescriptorFile)
if err != nil {
return err
}
Expand All @@ -43,12 +48,12 @@ func parseProtobufDescriptor() error {
}
var kyverdk string
for _, service := range services {
if service == "kyverdk.runtime.v1.RuntimeService" {
if service == ServiceName {
kyverdk = service
}
}
if kyverdk == "" {
return fmt.Errorf("kyverdk.runtime.v1.RuntimeService not found")
return fmt.Errorf("%s not found", ServiceName)
}

serviceDescriptor, err := sets.FindSymbol(kyverdk)
Expand Down

0 comments on commit b0c8ab6

Please sign in to comment.