VOL-3934 - TLS connection support

This is a bit of a breaking change as the current voltctl config
file defines verify as a string when it should have been a bool
from the start.

depends on merge of https://gerrit.opencord.org/c/voltha-lib-go/+/23594

Change-Id: Idb1f90a6bc827a599f2290bd276604997aab44e8
diff --git a/internal/pkg/commands/command.go b/internal/pkg/commands/command.go
index d2951a3..3e1beb4 100644
--- a/internal/pkg/commands/command.go
+++ b/internal/pkg/commands/command.go
@@ -16,12 +16,13 @@
 package commands
 
 import (
+	"context"
+	"crypto/tls"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"io/ioutil"
 	"log"
-	"net"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -32,11 +33,14 @@
 
 	"github.com/golang/protobuf/jsonpb"
 	"github.com/golang/protobuf/proto"
+	configv1 "github.com/opencord/voltctl/internal/pkg/apis/config/v1"
+	configv2 "github.com/opencord/voltctl/internal/pkg/apis/config/v2"
 	"github.com/opencord/voltctl/pkg/filter"
 	"github.com/opencord/voltctl/pkg/format"
 	"github.com/opencord/voltctl/pkg/order"
 	"google.golang.org/grpc"
-	"gopkg.in/yaml.v2"
+	"google.golang.org/grpc/credentials"
+	yaml "gopkg.in/yaml.v2"
 )
 
 type OutputType uint8
@@ -46,48 +50,9 @@
 	OUTPUT_JSON
 	OUTPUT_YAML
 
-	defaultApiHost = "localhost"
-	defaultApiPort = 55555
-
-	defaultKafkaHost = "localhost"
-	defaultKafkaPort = 9092
-
 	supportedKvStoreType = "etcd"
-	defaultKvHost        = "localhost"
-	defaultKvPort        = 2379
-	defaultKvTimeout     = time.Second * 5
-
-	defaultGrpcTimeout            = time.Minute * 5
-	defaultGrpcMaxCallRecvMsgSize = "4MB"
 )
 
-type GrpcConfigSpec struct {
-	Timeout            time.Duration `yaml:"timeout"`
-	MaxCallRecvMsgSize string        `yaml:"maxCallRecvMsgSize"`
-}
-
-type KvStoreConfigSpec struct {
-	Timeout time.Duration `yaml:"timeout"`
-}
-
-type TlsConfigSpec struct {
-	UseTls bool   `yaml:"useTls"`
-	CACert string `yaml:"caCert"`
-	Cert   string `yaml:"cert"`
-	Key    string `yaml:"key"`
-	Verify string `yaml:"verify"`
-}
-
-type GlobalConfigSpec struct {
-	Server        string            `yaml:"server"`
-	Kafka         string            `yaml:"kafka"`
-	KvStore       string            `yaml:"kvstore"`
-	Tls           TlsConfigSpec     `yaml:"tls"`
-	Grpc          GrpcConfigSpec    `yaml:"grpc"`
-	KvStoreConfig KvStoreConfigSpec `yaml:"kvstoreconfig"`
-	K8sConfig     string            `yaml:"-"`
-}
-
 var (
 	ParamNames = map[string]map[string]string{
 		"v1": {
@@ -105,21 +70,7 @@
 
 	CharReplacer = strings.NewReplacer("\\t", "\t", "\\n", "\n")
 
-	GlobalConfig = GlobalConfigSpec{
-		Server:  "localhost:55555",
-		Kafka:   "",
-		KvStore: "localhost:2379",
-		Tls: TlsConfigSpec{
-			UseTls: false,
-		},
-		Grpc: GrpcConfigSpec{
-			Timeout:            defaultGrpcTimeout,
-			MaxCallRecvMsgSize: defaultGrpcMaxCallRecvMsgSize,
-		},
-		KvStoreConfig: KvStoreConfigSpec{
-			Timeout: defaultKvTimeout,
-		},
-	}
+	GlobalConfig = configv2.NewDefaultConfig()
 
 	GlobalCommandOptions = make(map[string]map[string]string)
 
@@ -194,31 +145,6 @@
 	}
 }
 
-func splitEndpoint(ep, defaultHost string, defaultPort int) (string, int, error) {
-	port := defaultPort
-	host, sPort, err := net.SplitHostPort(ep)
-	if err != nil {
-		if addrErr, ok := err.(*net.AddrError); ok {
-			if addrErr.Err != "missing port in address" {
-				return "", 0, err
-			}
-			host = ep
-		} else {
-			return "", 0, err
-		}
-	} else if len(strings.TrimSpace(sPort)) > 0 {
-		val, err := strconv.Atoi(sPort)
-		if err != nil {
-			return "", 0, err
-		}
-		port = val
-	}
-	if len(strings.TrimSpace(host)) == 0 {
-		host = defaultHost
-	}
-	return strings.Trim(host, "]["), port, nil
-}
-
 type CommandResult struct {
 	Format    format.Format
 	Filter    string
@@ -283,9 +209,15 @@
 			Error.Fatalf("Unable to read the configuration file '%s': %s",
 				GlobalOptions.Config, err.Error())
 		}
+		// First try the latest version of the config api then work
+		// backwards
 		if err = yaml.Unmarshal(configFile, &GlobalConfig); err != nil {
-			Error.Fatalf("Unable to parse the configuration file '%s': %s",
-				GlobalOptions.Config, err.Error())
+			GlobalConfigV1 := configv1.NewDefaultConfig()
+			if err = yaml.Unmarshal(configFile, &GlobalConfigV1); err != nil {
+				Error.Fatalf("Unable to parse the configuration file '%s': %s",
+					GlobalOptions.Config, err.Error())
+			}
+			GlobalConfig = configv2.FromConfigV1(GlobalConfigV1)
 		}
 	}
 
@@ -293,32 +225,22 @@
 	if GlobalOptions.Server != "" {
 		GlobalConfig.Server = GlobalOptions.Server
 	}
-	host, port, err := splitEndpoint(GlobalConfig.Server, defaultApiHost, defaultApiPort)
-	if err != nil {
-		Error.Fatalf("voltha API endport incorrectly specified '%s':%s",
-			GlobalConfig.Server, err)
+
+	if GlobalOptions.UseTLS {
+		GlobalConfig.Tls.UseTls = true
 	}
-	GlobalConfig.Server = net.JoinHostPort(host, strconv.Itoa(port))
+
+	if GlobalOptions.Verify {
+		GlobalConfig.Tls.Verify = true
+	}
 
 	if GlobalOptions.Kafka != "" {
 		GlobalConfig.Kafka = GlobalOptions.Kafka
 	}
-	host, port, err = splitEndpoint(GlobalConfig.Kafka, defaultKafkaHost, defaultKafkaPort)
-	if err != nil {
-		Error.Fatalf("Kafka endport incorrectly specified '%s':%s",
-			GlobalConfig.Kafka, err)
-	}
-	GlobalConfig.Kafka = net.JoinHostPort(host, strconv.Itoa(port))
 
 	if GlobalOptions.KvStore != "" {
 		GlobalConfig.KvStore = GlobalOptions.KvStore
 	}
-	host, port, err = splitEndpoint(GlobalConfig.KvStore, defaultKvHost, defaultKvPort)
-	if err != nil {
-		Error.Fatalf("KV store endport incorrectly specified '%s':%s",
-			GlobalConfig.KvStore, err)
-	}
-	GlobalConfig.KvStore = net.JoinHostPort(host, strconv.Itoa(port))
 
 	if GlobalOptions.KvStoreTimeout != "" {
 		timeout, err := time.ParseDuration(GlobalOptions.KvStoreTimeout)
@@ -384,7 +306,24 @@
 		Error.Fatalf("Cannot convert msgSize %s to bytes", GlobalConfig.Grpc.MaxCallRecvMsgSize)
 	}
 
-	return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(n))))
+	var opts []grpc.DialOption
+
+	opts = append(opts,
+		grpc.WithDisableRetry(),
+		grpc.WithBlock(),
+		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(n))))
+
+	if GlobalConfig.Tls.UseTls {
+		creds := credentials.NewTLS(&tls.Config{
+			InsecureSkipVerify: !GlobalConfig.Tls.Verify})
+		opts = append(opts, grpc.WithTransportCredentials(creds))
+	} else {
+		opts = append(opts, grpc.WithInsecure())
+	}
+	ctx, cancel := context.WithTimeout(context.TODO(),
+		GlobalConfig.Grpc.ConnectTimeout)
+	defer cancel()
+	return grpc.DialContext(ctx, GlobalConfig.Server, opts...)
 }
 
 func ConvertJsonProtobufArray(data_in interface{}) (string, error) {