VOL-2815 - for multiple target commands exit with error status if one fails

Change-Id: Idc2d3e9c5d40c66d462bf336058fafc39d592f9c
diff --git a/VERSION b/VERSION
index 8fc77d0..f8f3c08 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.17
+1.0.18
diff --git a/cmd/voltctl/voltctl.go b/cmd/voltctl/voltctl.go
index 02de992..cdee1fb 100644
--- a/cmd/voltctl/voltctl.go
+++ b/cmd/voltctl/voltctl.go
@@ -17,10 +17,11 @@
 
 import (
 	"fmt"
-	flags "github.com/jessevdk/go-flags"
-	"github.com/opencord/voltctl/internal/pkg/commands"
 	"os"
 	"path"
+
+	flags "github.com/jessevdk/go-flags"
+	"github.com/opencord/voltctl/internal/pkg/commands"
 )
 
 func main() {
@@ -77,10 +78,13 @@
 			if real.Type == flags.ErrHelp {
 				parser.WriteHelp(os.Stdout)
 			} else {
-				fmt.Fprintf(os.Stderr, "%s\n", real.Error())
+				if real.Error() != commands.NoReportErr.Error() {
+					fmt.Fprintf(os.Stderr, "%s\n", real.Error())
+				}
+				os.Exit(1)
 			}
 			return
-		} else {
+		} else if err != commands.NoReportErr {
 			commands.Error.Fatal(commands.ErrorToString(err))
 		}
 		os.Exit(1)
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index e6a2a3f..24e3554 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -18,14 +18,15 @@
 import (
 	"context"
 	"fmt"
+	"os"
+	"strconv"
+	"strings"
+
 	"github.com/fullstorydev/grpcurl"
 	flags "github.com/jessevdk/go-flags"
 	"github.com/jhump/protoreflect/dynamic"
 	"github.com/opencord/voltctl/pkg/format"
 	"github.com/opencord/voltctl/pkg/model"
-	"os"
-	"strconv"
-	"strings"
 )
 
 const (
@@ -393,6 +394,7 @@
 		return err
 	}
 
+	var lastErr error
 	for _, i := range options.Args.Ids {
 
 		h := &RpcEventHandler{
@@ -404,14 +406,19 @@
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
 			Error.Printf("Error while deleting '%s': %s\n", i, err)
+			lastErr = err
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
 			Error.Printf("Error while deleting '%s': %s\n", i, ErrorToString(h.Status.Err()))
+			lastErr = h.Status.Err()
 			continue
 		}
 		fmt.Printf("%s\n", i)
 	}
 
+	if lastErr != nil {
+		return NoReportErr
+	}
 	return nil
 }
 
@@ -427,6 +434,7 @@
 		return err
 	}
 
+	var lastErr error
 	for _, i := range options.Args.Ids {
 		h := &RpcEventHandler{
 			Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
@@ -437,14 +445,19 @@
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
 			Error.Printf("Error while enabling '%s': %s\n", i, err)
+			lastErr = err
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
 			Error.Printf("Error while enabling '%s': %s\n", i, ErrorToString(h.Status.Err()))
+			lastErr = h.Status.Err()
 			continue
 		}
 		fmt.Printf("%s\n", i)
 	}
 
+	if lastErr != nil {
+		return NoReportErr
+	}
 	return nil
 }
 
@@ -460,6 +473,7 @@
 		return err
 	}
 
+	var lastErr error
 	for _, i := range options.Args.Ids {
 		h := &RpcEventHandler{
 			Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
@@ -470,14 +484,19 @@
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
 			Error.Printf("Error while disabling '%s': %s\n", i, err)
+			lastErr = err
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
 			Error.Printf("Error while disabling '%s': %s\n", i, ErrorToString(h.Status.Err()))
+			lastErr = h.Status.Err()
 			continue
 		}
 		fmt.Printf("%s\n", i)
 	}
 
+	if lastErr != nil {
+		return NoReportErr
+	}
 	return nil
 }
 
@@ -493,6 +512,7 @@
 		return err
 	}
 
+	var lastErr error
 	for _, i := range options.Args.Ids {
 		h := &RpcEventHandler{
 			Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
@@ -503,14 +523,19 @@
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
 			Error.Printf("Error while rebooting '%s': %s\n", i, err)
+			lastErr = err
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
 			Error.Printf("Error while rebooting '%s': %s\n", i, ErrorToString(h.Status.Err()))
+			lastErr = h.Status.Err()
 			continue
 		}
 		fmt.Printf("%s\n", i)
 	}
 
+	if lastErr != nil {
+		return NoReportErr
+	}
 	return nil
 }
 
diff --git a/internal/pkg/commands/error.go b/internal/pkg/commands/error.go
index ff641d3..d198a5c 100644
--- a/internal/pkg/commands/error.go
+++ b/internal/pkg/commands/error.go
@@ -18,16 +18,19 @@
 package commands
 
 import (
+	"errors"
 	"fmt"
-	"google.golang.org/grpc/status"
 	"regexp"
 	"strings"
+
+	"google.golang.org/grpc/status"
 )
 
 var (
-	descRE = regexp.MustCompile(`desc = "(.*)"`)
-	try2RE = regexp.MustCompile(`all SubConns are in TransientFailure, latest connection error: (.*)`)
-	try3RE = regexp.MustCompile(`all SubConns are in TransientFailure, (.*)`)
+	descRE      = regexp.MustCompile(`desc = "(.*)"`)
+	try2RE      = regexp.MustCompile(`all SubConns are in TransientFailure, latest connection error: (.*)`)
+	try3RE      = regexp.MustCompile(`all SubConns are in TransientFailure, (.*)`)
+	NoReportErr = errors.New("no-report-please")
 )
 
 func ErrorToString(err error) string {