SEBA-699 validate core version

Change-Id: I2ee14504c71e9fb4ecb262c9247108261c76fad3
diff --git a/commands/backup.go b/commands/backup.go
index fc5d509..de435fb 100644
--- a/commands/backup.go
+++ b/commands/backup.go
@@ -64,7 +64,7 @@
 }
 
 func (options *BackupCreate) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -162,7 +162,7 @@
 }
 
 func (options *BackupRestore) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
diff --git a/commands/command.go b/commands/command.go
index bdd7dfb..91d44a9 100644
--- a/commands/command.go
+++ b/commands/command.go
@@ -37,6 +37,8 @@
 	OUTPUT_TABLE OutputType = iota
 	OUTPUT_JSON
 	OUTPUT_YAML
+
+	CORE_VERSION_CONSTRAINT = ">= 3, < 4" // Support XOS major version 3
 )
 
 // Make it easy to override output stream for testing
diff --git a/commands/common.go b/commands/common.go
index f07e705..bb26273 100644
--- a/commands/common.go
+++ b/commands/common.go
@@ -20,6 +20,8 @@
 	b64 "encoding/base64"
 	"fmt"
 	"github.com/fullstorydev/grpcurl"
+	versionUtils "github.com/hashicorp/go-version"
+	"github.com/jhump/protoreflect/dynamic"
 	"github.com/jhump/protoreflect/grpcreflect"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
@@ -30,6 +32,12 @@
 	"strings"
 )
 
+// Flags for calling the InitReflectionClient Method
+const (
+	INIT_DEFAULT          = 0
+	INIT_NO_VERSION_CHECK = 1 // Do not check whether server is allowed version
+)
+
 func GenerateHeaders() []string {
 	username := GlobalConfig.Username
 	password := GlobalConfig.Password
@@ -38,7 +46,34 @@
 	return headers
 }
 
-func InitReflectionClient() (*grpc.ClientConn, grpcurl.DescriptorSource, error) {
+// Perform the GetVersion API call on the core to get the version
+func GetVersion(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource) (*dynamic.Message, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	headers := GenerateHeaders()
+
+	h := &RpcEventHandler{}
+	err := grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.utility.GetVersion", headers, h, h.GetParams)
+	if err != nil {
+		return nil, err
+	}
+
+	if h.Status != nil && h.Status.Err() != nil {
+		return nil, h.Status.Err()
+	}
+
+	d, err := dynamic.AsDynamicMessage(h.Response)
+
+	return d, err
+}
+
+// Initialize client connection
+//    flags is a set of optional flags that may influence how the connection is setup
+//        INIT_DEFAULT - default behavior (0)
+//        INIT_NO_VERSION_CHECK - do not perform core version check
+
+func InitClient(flags uint32) (*grpc.ClientConn, grpcurl.DescriptorSource, error) {
 	conn, err := NewConnection()
 	if err != nil {
 		return nil, nil, err
@@ -61,6 +96,29 @@
 		descriptor = grpcurl.DescriptorSourceFromServer(context.Background(), refClient)
 	}
 
+	if flags&INIT_NO_VERSION_CHECK == 0 {
+		d, err := GetVersion(conn, descriptor)
+		if err != nil {
+			return nil, nil, err
+		}
+		// Note: NewVersion doesn't like the `-dev` suffix, so strip it off.
+		serverVersion, err := versionUtils.NewVersion(strings.Split(d.GetFieldByName("version").(string), "-")[0])
+		if err != nil {
+			return nil, nil, err
+		}
+
+		constraint, err := versionUtils.NewConstraint(CORE_VERSION_CONSTRAINT)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		if !constraint.Check(serverVersion) {
+			return nil, nil, fmt.Errorf("Core version %s does not match constraint '%s'",
+				serverVersion, CORE_VERSION_CONSTRAINT)
+		}
+
+	}
+
 	return conn, descriptor, nil
 }
 
diff --git a/commands/models.go b/commands/models.go
index f95177e..49e85d8 100644
--- a/commands/models.go
+++ b/commands/models.go
@@ -248,7 +248,7 @@
 }
 
 func (options *ModelList) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -339,7 +339,7 @@
 }
 
 func (options *ModelUpdate) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -439,7 +439,7 @@
 }
 
 func (options *ModelDelete) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -466,7 +466,7 @@
 }
 
 func (options *ModelCreate) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -525,7 +525,7 @@
 }
 
 func (options *ModelSync) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -557,7 +557,7 @@
 }
 
 func (options *ModelSetDirty) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -585,7 +585,7 @@
 }
 
 func (modelName *ModelNameString) Complete(match string) []flags.Completion {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return nil
 	}
diff --git a/commands/modeltypes.go b/commands/modeltypes.go
index 4d2153a..e24a57a 100644
--- a/commands/modeltypes.go
+++ b/commands/modeltypes.go
@@ -40,7 +40,7 @@
 }
 
 func (options *ModelTypeList) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
diff --git a/commands/orm_test.go b/commands/orm_test.go
index 7ab30bf..1050de1 100644
--- a/commands/orm_test.go
+++ b/commands/orm_test.go
@@ -70,7 +70,7 @@
 }
 
 func TestTypeConvert(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -88,7 +88,7 @@
 }
 
 func TestCheckModelName(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -100,7 +100,7 @@
 }
 
 func TestCreateModel(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -115,7 +115,7 @@
 }
 
 func TestUpdateModel(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -128,7 +128,7 @@
 }
 
 func TestGetModel(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -140,7 +140,7 @@
 }
 
 func TestListModels(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -155,7 +155,7 @@
 }
 
 func TestFilterModels(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
@@ -170,7 +170,7 @@
 }
 
 func TestDeleteModel(t *testing.T) {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	assert.Equal(t, err, nil)
 	defer conn.Close()
 
diff --git a/commands/services.go b/commands/services.go
index e4a11cf..cc99dc5 100644
--- a/commands/services.go
+++ b/commands/services.go
@@ -48,7 +48,7 @@
 }
 
 func (options *ServiceList) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
diff --git a/commands/status.go b/commands/status.go
index 215d1bb..0924db4 100644
--- a/commands/status.go
+++ b/commands/status.go
@@ -42,7 +42,7 @@
 }
 
 func (options *StatusListOpts) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
diff --git a/commands/transfer.go b/commands/transfer.go
index 59be175..229f0fd 100644
--- a/commands/transfer.go
+++ b/commands/transfer.go
@@ -66,7 +66,7 @@
 
 func (options *TransferUpload) Execute(args []string) error {
 
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
@@ -110,7 +110,7 @@
 }
 
 func (options *TransferDownload) Execute(args []string) error {
-	conn, descriptor, err := InitReflectionClient()
+	conn, descriptor, err := InitClient(INIT_DEFAULT)
 	if err != nil {
 		return err
 	}
diff --git a/commands/version.go b/commands/version.go
index 44d7213..d650c59 100644
--- a/commands/version.go
+++ b/commands/version.go
@@ -17,11 +17,7 @@
 package commands
 
 import (
-	"context"
-
-	"github.com/fullstorydev/grpcurl"
 	flags "github.com/jessevdk/go-flags"
-	"github.com/jhump/protoreflect/dynamic"
 	"github.com/opencord/cordctl/cli/version"
 	"github.com/opencord/cordctl/format"
 )
@@ -105,28 +101,13 @@
 
 func (options *VersionOpts) Execute(args []string) error {
 	if !options.ClientOnly {
-		conn, descriptor, err := InitReflectionClient()
+		conn, descriptor, err := InitClient(INIT_NO_VERSION_CHECK)
 		if err != nil {
 			return err
 		}
 		defer conn.Close()
 
-		ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
-		defer cancel()
-
-		headers := GenerateHeaders()
-
-		h := &RpcEventHandler{}
-		err = grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.utility.GetVersion", headers, h, h.GetParams)
-		if err != nil {
-			return err
-		}
-
-		if h.Status != nil && h.Status.Err() != nil {
-			return h.Status.Err()
-		}
-
-		d, err := dynamic.AsDynamicMessage(h.Response)
+		d, err := GetVersion(conn, descriptor)
 		if err != nil {
 			return err
 		}