Add maxCallRecvMsgSize parameter and read it from the configuration
GRPC client max msg receive size
file. It allows to set if the incoming message is bigger than the
default 4K size.

Change-Id: I5b5113264066b84b5b055faf7d4bc02a3f11ec89
diff --git a/internal/pkg/commands/command.go b/internal/pkg/commands/command.go
index 9b2afd0..d5a2785 100644
--- a/internal/pkg/commands/command.go
+++ b/internal/pkg/commands/command.go
@@ -19,6 +19,17 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"io/ioutil"
+	"log"
+	"net"
+	"os"
+	"path/filepath"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+
 	"github.com/golang/protobuf/jsonpb"
 	"github.com/golang/protobuf/proto"
 	"github.com/opencord/voltctl/pkg/filter"
@@ -26,15 +37,6 @@
 	"github.com/opencord/voltctl/pkg/order"
 	"google.golang.org/grpc"
 	"gopkg.in/yaml.v2"
-	"io/ioutil"
-	"log"
-	"net"
-	"os"
-	"path/filepath"
-	"reflect"
-	"strconv"
-	"strings"
-	"time"
 )
 
 type OutputType uint8
@@ -55,11 +57,13 @@
 	defaultKvPort        = 2379
 	defaultKvTimeout     = time.Second * 5
 
-	defaultGrpcTimeout = time.Minute * 5
+	defaultGrpcTimeout            = time.Minute * 5
+	defaultGrpcMaxCallRecvMsgSize = "4MB"
 )
 
 type GrpcConfigSpec struct {
-	Timeout time.Duration `yaml:"timeout"`
+	Timeout            time.Duration `yaml:"timeout"`
+	MaxCallRecvMsgSize string        `yaml:"maxCallRecvMsgSize"`
 }
 
 type KvStoreConfigSpec struct {
@@ -109,7 +113,8 @@
 			UseTls: false,
 		},
 		Grpc: GrpcConfigSpec{
-			Timeout: defaultGrpcTimeout,
+			Timeout:            defaultGrpcTimeout,
+			MaxCallRecvMsgSize: defaultGrpcMaxCallRecvMsgSize,
 		},
 		KvStoreConfig: KvStoreConfigSpec{
 			Timeout: defaultKvTimeout,
@@ -125,16 +130,17 @@
 		KvStore string `short:"e" long:"kvstore" env:"KVSTORE" value-name:"SERVER:PORT" description:"IP/Host and port of KV store (etcd)"`
 
 		// nolint: staticcheck
-		Debug          bool   `short:"d" long:"debug" description:"Enable debug mode"`
-		Timeout        string `short:"t" long:"timeout" description:"API call timeout duration" value-name:"DURATION" default:""`
-		UseTLS         bool   `long:"tls" description:"Use TLS"`
-		CACert         string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
-		Cert           string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
-		Key            string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
-		Verify         bool   `long:"tlsverify" description:"Use TLS and verify the remote"`
-		K8sConfig      string `short:"8" long:"k8sconfig" env:"KUBECONFIG" value-name:"FILE" default:"" description:"Location of Kubernetes config file"`
-		KvStoreTimeout string `long:"kvstoretimeout" env:"KVSTORE_TIMEOUT" value-name:"DURATION" default:"" description:"timeout for calls to KV store"`
-		CommandOptions string `short:"o" long:"command-options" env:"VOLTCTL_COMMAND_OPTIONS" value-name:"FILE" default:"" description:"Location of command options default configuration file"`
+		Debug              bool   `short:"d" long:"debug" description:"Enable debug mode"`
+		Timeout            string `short:"t" long:"timeout" description:"API call timeout duration" value-name:"DURATION" default:""`
+		UseTLS             bool   `long:"tls" description:"Use TLS"`
+		CACert             string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
+		Cert               string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
+		Key                string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
+		Verify             bool   `long:"tlsverify" description:"Use TLS and verify the remote"`
+		K8sConfig          string `short:"8" long:"k8sconfig" env:"KUBECONFIG" value-name:"FILE" default:"" description:"Location of Kubernetes config file"`
+		KvStoreTimeout     string `long:"kvstoretimeout" env:"KVSTORE_TIMEOUT" value-name:"DURATION" default:"" description:"timeout for calls to KV store"`
+		CommandOptions     string `short:"o" long:"command-options" env:"VOLTCTL_COMMAND_OPTIONS" value-name:"FILE" default:"" description:"Location of command options default configuration file"`
+		MaxCallRecvMsgSize string `short:"m" long:"maxcallrecvmsgsize" description:"Max GRPC Client request size limit in bytes (eg: 4MB)" value-name:"SIZE" default:"4M"`
 	}
 
 	Debug = log.New(os.Stdout, "DEBUG: ", 0)
@@ -227,6 +233,36 @@
 	return defaultValue
 }
 
+var sizeParser = regexp.MustCompile(`^([0-9]+)([PETGMK]?)I?B?$`)
+
+func parseSize(size string) (uint64, error) {
+
+	parts := sizeParser.FindAllStringSubmatch(strings.ToUpper(size), -1)
+	if len(parts) == 0 {
+		return 0, fmt.Errorf("size: invalid size '%s'", size)
+	}
+	value, err := strconv.ParseUint(parts[0][1], 10, 64)
+	if err != nil {
+		return 0, fmt.Errorf("size: invalid size '%s'", size)
+	}
+	switch parts[0][2] {
+	case "E":
+		value = value * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
+	case "P":
+		value = value * 1024 * 1024 * 1024 * 1024 * 1024
+	case "T":
+		value = value * 1024 * 1024 * 1024 * 1024
+	case "G":
+		value = value * 1024 * 1024 * 1024
+	case "M":
+		value = value * 1024 * 1024
+	case "K":
+		value = value * 1024
+	default:
+	}
+	return value, nil
+}
+
 func ProcessGlobalOptions() {
 	if len(GlobalOptions.Config) == 0 {
 		home, err := os.UserHomeDir()
@@ -298,6 +334,10 @@
 		GlobalConfig.Grpc.Timeout = timeout
 	}
 
+	if GlobalOptions.MaxCallRecvMsgSize != "" {
+		GlobalConfig.Grpc.MaxCallRecvMsgSize = GlobalOptions.MaxCallRecvMsgSize
+	}
+
 	// If a k8s cert/key were not specified, then attempt to read it from
 	// any $HOME/.kube/config if it exists
 	if len(GlobalOptions.K8sConfig) == 0 {
@@ -333,7 +373,14 @@
 
 func NewConnection() (*grpc.ClientConn, error) {
 	ProcessGlobalOptions()
-	return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure())
+
+	// convert grpc.msgSize into bytes
+	n, err := parseSize(GlobalConfig.Grpc.MaxCallRecvMsgSize)
+	if err != nil {
+		Error.Fatalf("Cannot convert msgSize %s to bytes", GlobalConfig.Grpc.MaxCallRecvMsgSize)
+	}
+
+	return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(n))))
 }
 
 func ConvertJsonProtobufArray(data_in interface{}) (string, error) {