From 7d870385c5b132e5044eb14a2f892ebf54157551 Mon Sep 17 00:00:00 2001 From: pnickolov Date: Thu, 16 Feb 2023 23:23:11 -0800 Subject: [PATCH] fixed usage help and error handling for solutions and objstore (#26) --- cmd/objstore/create.go | 48 ++++++++++++----------------- cmd/objstore/delete.go | 31 ++++++++----------- cmd/objstore/get.go | 9 ++---- cmd/objstore/objstore.go | 5 ---- cmd/objstore/update.go | 19 +++++------- cmd/solution/author.go | 18 +++++------ cmd/solution/check.go | 26 ++++++++-------- cmd/solution/download.go | 13 ++++---- cmd/solution/extend.go | 43 +++++++++++++------------- cmd/solution/fork.go | 2 +- cmd/solution/init.go | 60 +++++++++++++++++-------------------- cmd/solution/package.go | 31 +++++++++++-------- cmd/solution/push.go | 39 +++++++++++++----------- cmd/solution/status.go | 14 ++++----- cmd/solution/subscribe.go | 9 +++--- cmd/solution/unsubscribe.go | 9 +++--- cmd/solution/validate.go | 17 +++++------ 17 files changed, 177 insertions(+), 216 deletions(-) diff --git a/cmd/objstore/create.go b/cmd/objstore/create.go index f2bd4ee7..9b77995a 100644 --- a/cmd/objstore/create.go +++ b/cmd/objstore/create.go @@ -16,12 +16,14 @@ package objstore import ( "encoding/json" + "fmt" "io" "os" "github.com/apex/log" "github.com/spf13/cobra" + "github.com/cisco-open/fsoc/output" "github.com/cisco-open/fsoc/platform/api" ) @@ -30,14 +32,9 @@ var objStoreInsertCmd = &cobra.Command{ Short: "Create a new object of a given type", Long: `This command allows the creation of a new object of a given type in the Object Store. - Usage: - fsoc objstore create --type --object-file= --layer-type= [--layer-id=] - - Flags/Options: - --type - Flag to indicate the fully qualified type name of the object that you would like to create - --object-file - Flag to indicate the fully qualified path (from your root directory) to the file containing the definition of the object that you want to create - --layer-type - Flag to indicate the layer at which you would like to create your object - --layer-id - OPTIONAL Flag to specify a custom layer ID for the object that you would like to create. This is calculated automatically for all layers currently supported but can be overridden with this flag`, +Example: + fsoc objstore create --type --object-file= --layer-type= [--layer-id=] +`, Args: cobra.ExactArgs(0), Run: insertObject, @@ -50,7 +47,7 @@ func getCreateObjectCmd() *cobra.Command { _ = objStoreInsertCmd.MarkPersistentFlagRequired("type") objStoreInsertCmd.Flags(). - String("object-file", "", "The fully qualified path to the json file containing the object definition") + String("object-file", "", "The fully qualified path to the json file containing the object data") _ = objStoreInsertCmd.MarkPersistentFlagRequired("objectFile") objStoreInsertCmd.Flags(). @@ -70,8 +67,7 @@ func insertObject(cmd *cobra.Command, args []string) { objJsonFilePath, _ := cmd.Flags().GetString("object-file") objectFile, err := os.Open(objJsonFilePath) if err != nil { - log.Errorf("Can't find the object definition file named %s", objJsonFilePath) - return + log.Fatalf("Can't find the object definition file named %q", objJsonFilePath) } defer objectFile.Close() @@ -79,8 +75,7 @@ func insertObject(cmd *cobra.Command, args []string) { var objectStruct map[string]interface{} err = json.Unmarshal(objectBytes, &objectStruct) if err != nil { - log.Errorf("Can't generate a %s object from the %s file. Make sure the object definition has all the required field and is valid according to the type definition.") - return + log.Fatalf("Failed to parse object data from file %q: %v. Make sure the object definition has all the required field and is valid according to the type definition.", objJsonFilePath, err) } layerType, _ := cmd.Flags().GetString("layer-type") @@ -88,13 +83,11 @@ func insertObject(cmd *cobra.Command, args []string) { if layerID == "" { if !cmd.Flags().Changed("layer-id") { - log.Error("Unable to set layer-id flag from given context. Please specify a unique layer-id value with the --layer-id flag") - return + log.Fatal("Unable to set layer-id flag from given context. Please specify a unique layer-id value with the --layer-id flag") } layerID, err = cmd.Flags().GetString("layer-id") if err != nil { - log.Errorf("error trying to get %q flag value: %w", "layer-id", err) - return + log.Fatalf("error trying to get %q flag value: %w", "layer-id", err) } } @@ -107,10 +100,9 @@ func insertObject(cmd *cobra.Command, args []string) { // objJsonStr, err := json.Marshal(objectStruct) err = api.JSONPost(getObjStoreObjectUrl()+"/"+objType, objectStruct, &res, &api.Options{Headers: headers}) if err != nil { - log.Errorf("objstore command failed: %v", err.Error()) - return + log.Fatalf("Failed to create object: %v", err) } else { - log.Infof("Successfully created %s object", objType) + log.Infof("Successfully created a %q object", objType) } } @@ -122,11 +114,10 @@ var objStoreInsertPatchedObjectCmd = &cobra.Command{ Use: "create-patch", Short: "Create a new patched object of a given type", Long: `This command allows the creation of a new patched object of a given type in the Object Store. - A patched object inherits values from an object that exists at a higher layer and can also override mutable fields when needed. - +A patched object inherits values from an object that exists at a higher layer and can also override mutable fields when needed. - Usage: - fsoc objstore create-patch --type --object-file= --target-layer-type= --parent-object-id=`, +Example: + fsoc objstore create-patch --type --object-file= --target-layer-type= --parent-object-id=`, Args: cobra.ExactArgs(0), Run: insertPatchObject, @@ -160,7 +151,7 @@ func insertPatchObject(cmd *cobra.Command, args []string) { objJsonFilePath, _ := cmd.Flags().GetString("object-file") objectFile, err := os.Open(objJsonFilePath) if err != nil { - log.Errorf("Can't find the object definition file named %s", objJsonFilePath) + log.Fatalf("Can't find the object definition file %q", objJsonFilePath) return } defer objectFile.Close() @@ -169,8 +160,7 @@ func insertPatchObject(cmd *cobra.Command, args []string) { var objectStruct map[string]interface{} err = json.Unmarshal(objectBytes, &objectStruct) if err != nil { - log.Errorf("Can't generate a %s object from the %s file. Make sure the object definition has all the required fields and is valid according to the type definition.") - return + log.Fatalf("Failed to parse object data from file %q: %v. Make sure the object definition has all the required fields and is valid according to the type definition.", objJsonFilePath, err) } layerType, _ := cmd.Flags().GetString("target-layer-type") @@ -184,9 +174,9 @@ func insertPatchObject(cmd *cobra.Command, args []string) { var res any err = api.JSONPatch(getObjStoreObjectUrl()+"/"+objType+"/"+parentObjId, objectStruct, &res, &api.Options{Headers: headers}) if err != nil { - log.Errorf("Creating a patched object command failed: %v", err.Error()) + log.Fatalf("Failed to create object: %v", err) return } else { - log.Infof("Successfully created patched %s object at the %s layer", objType, layerType) + output.PrintCmdOutput(cmd, fmt.Sprintf("Successfully created a patched %q object at the %s layer.\n", objType, layerType)) } } diff --git a/cmd/objstore/delete.go b/cmd/objstore/delete.go index ded07227..9550b69a 100644 --- a/cmd/objstore/delete.go +++ b/cmd/objstore/delete.go @@ -29,17 +29,13 @@ var objStoreDeleteCmd = &cobra.Command{ Short: "Delete an existent knowledge object", Long: `This command allows an existent knowledge object to be deleted. - Usage: - fsoc objstore delete --type= - --object-id= - --layer-type=[SOLUTION|ACCOUNT|GLOBALUSER|TENANT|LOCALUSER] - --layer-id= - - Flags/Options: - --type - Flag to indicate the fully qualified type name of the object that you would like to delete - --object-id - Flag to indicate the ID of the object which you would like to delete - --layer-type - Flag to indicate the layer at which the object you would like to delete currently exists - --layer-id - OPTIONAL Flag to specify a custom layer ID for the object that you would like to delete. This is calculated automatically for all layers currently supported but can be overridden with this flag`, +Usage: + fsoc objstore delete \ + --type= \ + --object-id= \ + --layer-type=[SOLUTION|ACCOUNT|GLOBALUSER|TENANT|LOCALUSER] \ + --layer-id= +`, Args: cobra.ExactArgs(0), Run: deleteObject, @@ -76,13 +72,11 @@ func deleteObject(cmd *cobra.Command, args []string) { if layerID == "" { if !cmd.Flags().Changed("layer-id") { - log.Error("Unable to set layer-id flag from given context. Please specify a unique layer-id value with the --layer-id flag") - return + log.Fatalf("Unable to set layer-id flag from given context. Please specify a unique layer-id value with the --layer-id flag") } layerID, err = cmd.Flags().GetString("layer-id") if err != nil { - log.Errorf("error trying to get %q flag value: %w", "layer-id", err) - return + log.Fatalf("error trying to get %q flag value: %w", "layer-id", err) } } @@ -96,11 +90,10 @@ func deleteObject(cmd *cobra.Command, args []string) { urlStrf := getObjStoreObjectUrl() + "/%s/%s" objectUrl := fmt.Sprintf(urlStrf, objType, objId) - output.PrintCmdStatus(cmd, (fmt.Sprintf("Deleting object %s of type %s \n", objId, objType))) + output.PrintCmdStatus(cmd, (fmt.Sprintf("Deleting object %q of type %q\n", objId, objType))) err = api.JSONDelete(objectUrl, &res, &api.Options{Headers: headers}) if err != nil { - log.Errorf("Solution command failed: %v", err.Error()) - return + log.Fatalf("Failed to delete object: %v", err) } - output.PrintCmdStatus(cmd, "Object was successfully deleted!\n") + output.PrintCmdStatus(cmd, "Object was successfully deleted.\n") } diff --git a/cmd/objstore/get.go b/cmd/objstore/get.go index 5fea773a..8279a526 100644 --- a/cmd/objstore/get.go +++ b/cmd/objstore/get.go @@ -69,8 +69,6 @@ func newGetObjectCmd() *cobra.Command { //_ = getCmd.MarkPersistentFlagRequired("layer-id") _ = getCmd.MarkPersistentFlagRequired("layer-type") - _ = getCmd.MarkPersistentFlagRequired("type") - return getCmd } @@ -130,7 +128,7 @@ func getObject(cmd *cobra.Command, args []string, ltFlag layerType) error { layerID, _ := cmd.Flags().GetString("layer-id") if layerID == "" { if layerType == "SOLUTION" { - return fmt.Errorf("Error: for GET requests made to the SOLUTION layer, please manually supply the layerId flag") + return fmt.Errorf("Requests made to the SOLUTION layer require the --layer-id flag") } else { layerID = getCorrectLayerID(layerType, fqtn) } @@ -148,11 +146,10 @@ func getObject(cmd *cobra.Command, args []string, ltFlag layerType) error { } else { if cmd.Flags().Changed("filter") { filterCriteria, err := cmd.Flags().GetString("filter") - query := fmt.Sprintf("filter=%s", url.QueryEscape(filterCriteria)) if err != nil { - log.Errorf("error trying to get %q flag value: %w", "filter", err) - return nil + return fmt.Errorf("error trying to get %q flag value: %w", "filter", err) } + query := fmt.Sprintf("filter=%s", url.QueryEscape(filterCriteria)) fqtn = fqtn + "?" + query } objStoreUrl = getObjectListUrl(fqtn) diff --git a/cmd/objstore/objstore.go b/cmd/objstore/objstore.go index 7af83c18..c28fa912 100644 --- a/cmd/objstore/objstore.go +++ b/cmd/objstore/objstore.go @@ -15,8 +15,6 @@ package objstore import ( - "fmt" - "github.com/spf13/cobra" ) @@ -43,9 +41,6 @@ See `, fsoc obj get --type= --object= --layer-id= --layer-type=SOLUTION|ACCOUNT|GLOBALUSER|TENANT|LOCALUSER # Get object fsoc obj create --type= --object-file= --layer-type=SOLUTION|ACCOUNT|GLOBALUSER|TENANT|LOCALUSER [--layer-id=] `, - RunE: func(cmd *cobra.Command, args []string) error { - return fmt.Errorf("incomplete command") - }, TraverseChildren: true, } diff --git a/cmd/objstore/update.go b/cmd/objstore/update.go index 8bf8c275..e556fec7 100644 --- a/cmd/objstore/update.go +++ b/cmd/objstore/update.go @@ -81,8 +81,7 @@ func updateObject(cmd *cobra.Command, args []string) { objJsonFilePath, _ := cmd.Flags().GetString("object-file") objectFile, err := os.Open(objJsonFilePath) if err != nil { - log.Errorf("Can't find the object definition file named %s", objJsonFilePath) - return + log.Fatalf("Can't find the object definition file named %s", objJsonFilePath) } defer objectFile.Close() @@ -90,8 +89,7 @@ func updateObject(cmd *cobra.Command, args []string) { var objectStruct map[string]interface{} err = json.Unmarshal(objectBytes, &objectStruct) if err != nil { - log.Errorf("Can't generate a %s object from the %s file. Make sure the object definition has all the required field and is valid according to the type definition.") - return + log.Fatalf("Can't parse file %q. Make sure the object definition has all the required field and is valid according to the type definition.", objJsonFilePath) } layerType, _ := cmd.Flags().GetString("layer-type") @@ -99,13 +97,11 @@ func updateObject(cmd *cobra.Command, args []string) { if layerID == "" { if !cmd.Flags().Changed("layer-id") { - log.Error("Unable to set layer-id flag from given context. Please specify a unique layer-id value with the --layer-id flag") - return + log.Fatal("Unable to set layer-id flag from given context. Please specify a unique layer-id value with the --layer-id flag") } layerID, err = cmd.Flags().GetString("layer-id") if err != nil { - log.Errorf("error trying to get %q flag value: %w", "layer-id", err) - return + log.Fatalf("error trying to get %q flag value: %w", "layer-id", err) } } @@ -119,11 +115,10 @@ func updateObject(cmd *cobra.Command, args []string) { urlStrf := getObjStoreObjectUrl() + "/%s/%s" objectUrl := fmt.Sprintf(urlStrf, objType, objId) - output.PrintCmdStatus(cmd, fmt.Sprintf("Replacing object %s with the new definition from %s \n", objId, objJsonFilePath)) + output.PrintCmdStatus(cmd, fmt.Sprintf("Replacing object %q with the new data from %q \n", objId, objJsonFilePath)) err = api.JSONPut(objectUrl, objectStruct, &res, &api.Options{Headers: headers}) if err != nil { - log.Errorf("Solution command failed: %v", err.Error()) - return + log.Fatalf("Object update failed: %v", err) } - output.PrintCmdStatus(cmd, "Object replacement was done successfully!\n") + output.PrintCmdStatus(cmd, "Object updated successfully.\n") } diff --git a/cmd/solution/author.go b/cmd/solution/author.go index 36bdd23f..d665e0a8 100644 --- a/cmd/solution/author.go +++ b/cmd/solution/author.go @@ -172,7 +172,7 @@ func newTemplateServer(dir string) *templateServer { } exists, err := afero.DirExists(fileSystem, templatePath) if err != nil || !exists { - log.Fatalf("Could not find %q directory specified in the manifest: %v", templatePath, err.Error()) + log.Fatalf("Could not find %q directory specified in the manifest: %v", templatePath, err) } // create a termination signal channel @@ -281,7 +281,7 @@ func (t *templateServer) callbackHandler(w http.ResponseWriter, r *http.Request) fmt.Fprint(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } if err != nil { - log.Errorf("Error while parsing request: %v", err.Error()) + log.Errorf("Error while parsing request: %v", err) fmt.Fprint(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) } if terminate { @@ -294,7 +294,7 @@ func (t *templateServer) readTemplate(w http.ResponseWriter, r *http.Request, ur file, err := afero.ReadFile(t.Fs, t.TemplatePath+URIParts[len(URIParts)-1]) log.Info("Getting file at " + t.TemplatePath + URIParts[len(URIParts)-1]) if err != nil { - log.Errorf("No such template file in this directory: %v", err.Error()) + log.Errorf("No such template file in this directory: %v", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return err } else { @@ -312,7 +312,7 @@ func (t *templateServer) readTemplate(w http.ResponseWriter, r *http.Request, ur func (t *templateServer) returnTemplateList(w http.ResponseWriter, r *http.Request) error { files, err := afero.ReadDir(t.Fs, t.TemplatePath) if err != nil { - log.Errorf("Cannot read the files: %v", err.Error()) + log.Errorf("Cannot read the files: %v", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return err } @@ -341,12 +341,12 @@ func (t *templateServer) updateTemplate(w http.ResponseWriter, r *http.Request, //var body string b, err := io.ReadAll(r.Body) if err != nil { - log.Errorf("Cannot read request body: %v", err.Error()) + log.Errorf("Cannot read request body: %v", err) } URIParts := strings.Split(uri.RequestURI(), "/") err = afero.WriteFile(t.Fs, t.TemplatePath+URIParts[len(URIParts)-1], b, 0644) if err != nil { - log.Errorf("Error writing to this file: %v", err.Error()) + log.Errorf("Error writing to this file: %v", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return err } else { @@ -359,7 +359,7 @@ func (t *templateServer) deleteTemplateFile(w http.ResponseWriter, r *http.Reque URIParts := strings.Split(uri.RequestURI(), "/") err := t.Fs.Remove(t.TemplatePath + URIParts[len(URIParts)-1]) if err != nil { - log.Errorf("Error in deleting file: %v", err.Error()) + log.Errorf("Error in deleting file: %v", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return err } @@ -370,13 +370,13 @@ func (t *templateServer) deleteTemplateFile(w http.ResponseWriter, r *http.Reque func checkURI(w http.ResponseWriter, r *http.Request) (*url.URL, error) { _, err := url.Parse(callbackUrl) if err != nil { - log.Errorf("Unexpected failure to obtain expected callback path (likely a bug): %v", err.Error()) + log.Errorf("Unexpected failure to obtain expected callback path (likely a bug): %v", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return nil, err } uri, err := url.Parse(r.RequestURI) if err != nil { - log.Errorf("Unexpected failure to parse callback path received (malformed request?): %v", err.Error()) + log.Errorf("Unexpected failure to parse callback path received (malformed request?): %v", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return nil, err } diff --git a/cmd/solution/check.go b/cmd/solution/check.go index c658bddd..1939aaed 100644 --- a/cmd/solution/check.go +++ b/cmd/solution/check.go @@ -21,8 +21,8 @@ var solutionCheckCmd = &cobra.Command{ Short: "Validate your solution component definitions", Long: `This command allows the current tenant specified in the profile to build and package a solution bundle to be deployed into the FSO Platform. -Usage: - fsoc solution check`, +Example: + fsoc solution check --entities --metrics`, Args: cobra.ExactArgs(0), Run: checkSolution, TraverseChildren: true, @@ -44,13 +44,15 @@ func checkSolution(cmd *cobra.Command, args []string) { manifestFile := openFile("manifest.json") defer manifestFile.Close() - manifestBytes, _ := io.ReadAll(manifestFile) - var manifest *Manifest + manifestBytes, err := io.ReadAll(manifestFile) + if err != nil { + log.Fatalf("Failed to read manifest.json: %v", err) + } + var manifest *Manifest err = json.Unmarshal(manifestBytes, &manifest) if err != nil { - output.PrintCmdStatus(cmd, "Can't generate a manifest objects from the manifest.json, make sure your manifest.json file is correct.") - return + log.Fatalf("Failed to parse manifest.json: %v", err) } cfg := config.GetCurrentContext() @@ -102,7 +104,7 @@ func checkComponentDef(cmd *cobra.Command, compDef ComponentDef, cfg *config.Con jsonSchema, err := json.Marshal(typeDef["jsonSchema"]) if err != nil { - log.Errorf("Couldn't marshal json object to []byte: %v", err) + log.Errorf("Couldn't marshal schema to json: %v", err) } schemaLoader := gojsonschema.NewStringLoader(string(jsonSchema)) @@ -120,12 +122,12 @@ func checkComponentDef(cmd *cobra.Command, compDef ComponentDef, cfg *config.Con var jsonArray []map[string]interface{} err = json.Unmarshal(compDefBytes, &jsonArray) if err != nil { - log.Errorf("Couldn't unmarshal json file content to object array: %v", err) + log.Errorf("Failed to parse component: %v", err) } for _, object := range jsonArray { jsonObject, err := json.Marshal(object) if err != nil { - log.Errorf("Couldn't marshal json object to []byte: %v", err) + log.Errorf("Couldn't marshal object to json: %v", err) } documentLoader := gojsonschema.NewStringLoader(string(jsonObject)) validate(cmd, schemaLoader, documentLoader, compDef) @@ -140,13 +142,13 @@ func checkComponentDef(cmd *cobra.Command, compDef ComponentDef, cfg *config.Con func validate(cmd *cobra.Command, schemaLoader, documentLoader gojsonschema.JSONLoader, compDef ComponentDef) { result, err := gojsonschema.Validate(schemaLoader, documentLoader) if err != nil { - panic(err.Error()) + log.Fatalf("Schema validation failed: %v", err) } if result.Valid() { - output.PrintCmdStatus(cmd, fmt.Sprintf("The components defined in the file %s are valid definitions of type %s \n", compDef.ObjectsFile, compDef.Type)) + output.PrintCmdStatus(cmd, fmt.Sprintf("The components defined in the file %q are valid definitions of type %q \n", compDef.ObjectsFile, compDef.Type)) } else { - output.PrintCmdStatus(cmd, fmt.Sprintf("The components defined in the file %s are invalid definitions of type %s ! \n", compDef.ObjectsFile, compDef.Type)) + output.PrintCmdStatus(cmd, fmt.Sprintf("The components defined in the file %q are invalid definitions of type %q ! \n", compDef.ObjectsFile, compDef.Type)) for _, desc := range result.Errors() { output.PrintCmdStatus(cmd, fmt.Sprintf("- %s\n", desc)) } diff --git a/cmd/solution/download.go b/cmd/solution/download.go index d3245fb2..9ab73f22 100644 --- a/cmd/solution/download.go +++ b/cmd/solution/download.go @@ -28,17 +28,14 @@ var solutionDownloadCmd = &cobra.Command{ Short: "Download solution", Long: `This command allows the current tenant specified in the profile to download a solution bundle archive into the current directory or the directory specified in command argument. - Command: fsoc solution download --name= - - Usage: - fsoc solution download --name=`, +Example: fsoc solution download --name=spacefleet`, Args: cobra.ExactArgs(0), Run: downloadSolution, TraverseChildren: true, } func getSolutionDownloadCmd() *cobra.Command { - solutionDownloadCmd.Flags().String("name", "", "name of the solution that needs to be downloaded") + solutionDownloadCmd.Flags().String("name", "", "name of the solution to download (required)") _ = solutionDownloadCmd.MarkFlagRequired("name") return solutionDownloadCmd } @@ -46,7 +43,7 @@ func getSolutionDownloadCmd() *cobra.Command { func downloadSolution(cmd *cobra.Command, args []string) { solutionName, _ := cmd.Flags().GetString("name") if solutionName == "" { - log.Fatalf("solution-name cannot be empty, use --name=") + log.Fatalf("Solution name cannot be empty, use --name=") } var solutionNameWithZipExtension = getSolutionNameWithZip(solutionName) @@ -58,10 +55,10 @@ func downloadSolution(cmd *cobra.Command, args []string) { httpOptions := api.Options{Headers: headers} bufRes := make([]byte, 0) if err := api.HTTPGet(getSolutionDownloadUrl(solutionName), &bufRes, &httpOptions); err != nil { - log.Fatalf("Solution download command failed: %v", err.Error()) + log.Fatalf("Solution download command failed: %v", err) } - message := fmt.Sprintf("Solution bundle %s was successfully downloaded in current directory.\r\n", solutionName) + message := fmt.Sprintf("Solution bundle %q downloaded successfully.\n", solutionName) output.PrintCmdStatus(cmd, message) } diff --git a/cmd/solution/extend.go b/cmd/solution/extend.go index 7865d055..074b52af 100644 --- a/cmd/solution/extend.go +++ b/cmd/solution/extend.go @@ -32,22 +32,20 @@ var solutionExtendCmd = &cobra.Command{ Short: "Extends your solution package by adding new components", Long: `This command allows you to easily add new components to your solution package. - Command: fsoc solution extend --add-knowledge= - - Options: - --add-service - Flag to add a new service component to the current solution package - --add-knowledge - Flag to add a new knowledge type component to the current solution package - --add-meltworkflow - Flag to add a new melt workflow component to the current solution package - --add-dash-ui - Flag to add a new user experience component to the current solution package - --add-metric - Flag to add a new metric type component to the current solution package - - Usage: - fsoc solution extend --add-knowledge=`, +Example: + fsoc solution extend --add-knowledge=`, Run: addSolutionComponent, TraverseChildren: true, } +// Planned options: +// --add-service - Flag to add a new service component to the current solution package +// --add-knowledge - Flag to add a new knowledge type component to the current solution package +// --add-meltworkflow - Flag to add a new melt workflow component to the current solution package +// --add-dash-ui - Flag to add a new user experience component to the current solution package +// --add-metric - Flag to add a new metric type component to the current solution package + func getSolutionExtendCmd() *cobra.Command { solutionExtendCmd.Flags(). String("add-service", "", "Add as a new service component definition to this solution") @@ -76,9 +74,12 @@ func addSolutionComponent(cmd *cobra.Command, args []string) { manifestFile := openFile("manifest.json") defer manifestFile.Close() - manifestBytes, _ := io.ReadAll(manifestFile) - var manifest *Manifest + manifestBytes, err := io.ReadAll(manifestFile) + if err != nil { + log.Fatalf("Failed to read solution manifest: %v", err) + } + var manifest *Manifest err = json.Unmarshal(manifestBytes, &manifest) if err != nil { log.Fatalf("Failed to parse solution manifest: %v", err) @@ -177,7 +178,7 @@ func addFmmMetric(cmd *cobra.Command, manifest *Manifest, folderName, componentN metricsBytes := readComponentDef(metricComponentDef) err := json.Unmarshal(metricsBytes, &metricsArray) if err != nil { - log.Errorf("Can't generate an array of entity definition objects from the %s file, make sure your %s file is correct.", filePath, filePath) + log.Fatalf("Can't generate an array of entity definition objects from the %q file, make sure it is correct.", filePath) return } } @@ -189,7 +190,6 @@ func addFmmMetric(cmd *cobra.Command, manifest *Manifest, folderName, componentN createComponentFile(metricsArray, folderName, fileName) output.PrintCmdStatus(cmd, "Updating the solution manifest\n") createSolutionManifestFile(".", manifest) - } func addFmmResourceMapping(cmd *cobra.Command, manifest *Manifest, folderName, entityName string) { @@ -210,8 +210,7 @@ func addFmmResourceMapping(cmd *cobra.Command, manifest *Manifest, folderName, e componentDefBytes := readComponentDef(resourceMapComponentDef) err := json.Unmarshal(componentDefBytes, &resourceMappingArray) if err != nil { - log.Errorf("Can't generate an array of resource mapping definition objects from the %s file, make sure your %s file is correct.", filePath, filePath) - return + log.Fatalf("Can't generate an array of resource mapping definition objects from the %q file, make sure it is correct.", filePath) } } @@ -250,8 +249,7 @@ func addFmmEntity(cmd *cobra.Command, manifest *Manifest, folderName, componentN err := json.Unmarshal(entitiesBytes, &entitiesArray) if err != nil { - log.Errorf("Can't generate an array of entity definition objects from the %s file, make sure your %s file is correct.", filePath, filePath) - return + log.Fatalf("Can't generate an array of entity definition objects from the %q file, make it is correct.", filePath) } } @@ -272,8 +270,7 @@ func getResourceMap(cmd *cobra.Command, entityName string, manifest *Manifest) * entityBytes := readComponentDef(entityComponentDef) err := json.Unmarshal(entityBytes, &entitiesArray) if err != nil { - log.Errorf("Can't generate an array of %s type definition objects from the %s file.", entityComponentDef.Type, entityComponentDef.ObjectsFile) - return nil + log.Fatalf("Can't generate an array of %q type definition objects from the %q file.", entityComponentDef.Type, entityComponentDef.ObjectsFile) } for _, entity := range entitiesArray { if entity.Name == entityName { @@ -283,7 +280,7 @@ func getResourceMap(cmd *cobra.Command, entityName string, manifest *Manifest) * entityType := fmt.Sprintf("%s:%s", manifest.Name, entityName) scopeFilterFields := make([]string, 0) attributeMaps := make(FmmNameMappings, 0) - displayName := fmt.Sprintf("Resource mapping configuration for the %s entity", entityType) + displayName := fmt.Sprintf("Resource mapping configuration for the %q entity", entityType) fmmTypeDef := &FmmTypeDef{ Namespace: namespace, Kind: "resourceMapping", @@ -307,7 +304,7 @@ func getResourceMap(cmd *cobra.Command, entityName string, manifest *Manifest) * } if !hasEntity { - message := fmt.Sprintf("A fmm:resourceMapping was not created! Could not find an fmm:entity named %s in this solution", entityName) + message := fmt.Sprintf("A fmm:resourceMapping was not created! Could not find an fmm:entity named %q in this solution", entityName) output.PrintCmdStatus(cmd, message) } return newResoureMapping diff --git a/cmd/solution/fork.go b/cmd/solution/fork.go index 22abfaaf..c3cd1904 100644 --- a/cmd/solution/fork.go +++ b/cmd/solution/fork.go @@ -149,7 +149,7 @@ func downloadSolutionZip(cmd *cobra.Command, solutionName string, forkName strin httpOptions := api.Options{Headers: headers} bufRes := make([]byte, 0) if err := api.HTTPGet(getSolutionDownloadUrl(solutionName), &bufRes, &httpOptions); err != nil { - log.Fatalf("Solution download command failed: %v", err.Error()) + log.Fatalf("Solution download failed: %v", err) } message = fmt.Sprintf("Solution bundle %s was successfully downloaded in the this directory.\r\n", solutionName) diff --git a/cmd/solution/init.go b/cmd/solution/init.go index 568269d2..cbe28fdf 100644 --- a/cmd/solution/init.go +++ b/cmd/solution/init.go @@ -29,39 +29,40 @@ import ( var solutionInitCmd = &cobra.Command{ Use: "init", - Short: "Creates a new solution package", - Long: `This command creates a skeleton of a new solution by given name. + Short: "Create a new solution", + Long: `This command creates a skeleton of a solution in the current directory. - Command: fsoc solution init --name= [options] - - Parameters: - name - Name of the solution - - Options: - include-service - Flag to include sample service component - include-metric - Flag to include sample metric type component - include-knowledge - Flag to include sample knowledge type component - include-meltworkflow - Flag to include sample melt workflow - include-dash-ui - Flag to include sample dash-ui template - - Usage: - fsoc solution init --name= [--include-service] [--include-knowldege]`, +Example: + fsoc solution init --name=testSolution --include-service --include-knowledge + +Creates a subdirectory named "testSolution" in the current directory and populates +it with a solution manifest and objects for it. The optional --include-... flags +define what objects are added to the solution. Once the solution is created, +the "solution extend" command can be used to add more objects.`, Run: generateSolutionPackage, Annotations: map[string]string{config.AnnotationForConfigBypass: ""}, TraverseChildren: true, } +// Planned options: +// include-service - Flag to include sample service component +// include-metric - Flag to include sample metric type component +// include-knowledge - Flag to include sample knowledge type component +// include-meltworkflow - Flag to include sample melt workflow +// include-dash-ui - Flag to include sample dash-ui template + func getInitSolutionCmd() *cobra.Command { solutionInitCmd.Flags(). - String("name", "", "The name of the new solution") + String("name", "", "The name of the new solution (required)") + _ = solutionInitCmd.MarkFlagRequired("name") + solutionInitCmd.Flags(). Bool("include-service", true, "Add a service component definition to this solution") solutionInitCmd.Flags(). Bool("include-knowledge", true, "Add a knowledge type definition to this solution") return solutionInitCmd - } func generateSolutionPackage(cmd *cobra.Command, args []string) { @@ -70,15 +71,13 @@ func generateSolutionPackage(cmd *cobra.Command, args []string) { solutionName = strings.ToLower(solutionName) if len(solutionName) == 0 { - log.Errorf("Parameter \"name\" is required") - return + log.Fatal("A non-empty flag \"--name\" is required.") } output.PrintCmdStatus(cmd, fmt.Sprintf("Preparing the %s solution package folder structure... \n", solutionName)) if err := os.Mkdir(solutionName, os.ModePerm); err != nil { - log.Errorf("Solution init failed - %v", err.Error()) - return + log.Fatalf("Solution init failed - %v", err) } manifest := createInitialSolutionManifest(solutionName) @@ -138,10 +137,8 @@ func createInitialSolutionManifest(solutionName string) *Manifest { func createSolutionManifestFile(folderName string, manifest *Manifest) { filepath := fmt.Sprintf("%s/manifest.json", folderName) manifestFile, err := os.Create(filepath) - if err != nil { - log.Errorf("Failed to create manifest.json %v", err.Error()) - return + log.Fatalf("Failed to create manifest.json: %v", err) } manifestJson, _ := json.Marshal(manifest) @@ -189,8 +186,7 @@ func createKnowledgeComponent(manifest *Manifest) *KnowledgeDef { func createComponentFile(compDef any, folderName string, fileName string) { if _, err := os.Stat(folderName); os.IsNotExist(err) { if err := os.Mkdir(folderName, os.ModePerm); err != nil { - log.Errorf("Create solution component file failed - %v", err.Error()) - return + log.Fatalf("Failed to create solution component directory %q: %v", folderName, err) } } @@ -198,8 +194,7 @@ func createComponentFile(compDef any, folderName string, fileName string) { svcFile, err := os.Create(filepath) if err != nil { - log.Errorf("Create solution component file failed - %v", err.Error()) - return + log.Fatalf("Failed to create solution component file %q: %v", folderName+"/"+fileName, err) } defer svcFile.Close() @@ -212,7 +207,7 @@ func createComponentFile(compDef any, folderName string, fileName string) { func appendFolder(folderName string) { if _, err := os.Stat(folderName); os.IsNotExist(err) { if err := os.Mkdir(folderName, os.ModePerm); err != nil { - log.Errorf("Error adding folder named %s - %v", folderName, err.Error()) + log.Fatalf("Error adding folder named %q: %v", folderName, err) } } } @@ -220,8 +215,7 @@ func appendFolder(folderName string) { func openFile(filePath string) *os.File { svcFile, err := os.Open(filePath) if err != nil { - log.Errorf("Can't open the file named %s \n", filePath) - return nil + log.Fatalf("Can't open the file named %q: %v", filePath, err) } return svcFile } @@ -230,7 +224,7 @@ func createFile(filePath string) { var svcFile *os.File var err error if svcFile, err = os.Create(filePath); err != nil { - log.Errorf("Can't create the file named %s - %v", filePath, err.Error()) + log.Fatalf("Can't create the file named %q: %v", filePath, err) } svcFile.Close() } diff --git a/cmd/solution/package.go b/cmd/solution/package.go index 29577e49..78459722 100644 --- a/cmd/solution/package.go +++ b/cmd/solution/package.go @@ -58,7 +58,11 @@ func packageSolution(cmd *cobra.Command, args []string) { if !isSolutionPackageRoot(solutionPackagePath) { log.Fatal("solution-package path doesn't point to a solution package root folder") } - manifest, _ := getSolutionManifest(solutionPackagePath) + manifest, err := getSolutionManifest(solutionPackagePath) + if err != nil { + log.Fatalf("Failed to read solution manifest: %v", err) + } + var message string message = fmt.Sprintf("Generating solution %s - %s bundle archive \n", manifest.Name, manifest.SolutionVersion) log.WithFields(log.Fields{ @@ -88,19 +92,19 @@ func generateZip(cmd *cobra.Command, sltnPackagePath string) *os.File { fsocWorkingDir, err := os.Getwd() if err != nil { - log.Errorf("Couldn't read fsoc working directory: %v", err) + log.Fatalf("Couldn't read the current working directory: %v", err) } solutionRootFolder := filepath.Dir(sltnPackagePath) err = os.Chdir(solutionRootFolder) if err != nil { - log.Errorf("Couldn't switch working folder to solution package folder: %v", err) + log.Fatalf("Couldn't switch working folder to solution package folder: %v", err) } defer func() { err := os.Chdir(fsocWorkingDir) if err != nil { - log.Errorf("Couldn't switch working folder back to fsoc working folder: %v", err) + log.Fatalf("Couldn't switch working folder back to starting working folder: %v", err) } }() @@ -113,7 +117,7 @@ func generateZip(cmd *cobra.Command, sltnPackagePath string) *os.File { return nil }) if err != nil { - log.Errorf("Error traversing the folder: %v", err.Error()) + log.Fatalf("Error traversing the folder: %v", err) } zipWriter.Close() @@ -123,7 +127,7 @@ func generateZip(cmd *cobra.Command, sltnPackagePath string) *os.File { func addFileToZip(zipWriter *zip.Writer, fileName string, info os.FileInfo) { newFile, err := os.Open(fileName) if err != nil { - log.Errorf("Couldn't open file %v", err.Error()) + log.Fatalf("Couldn't open file %q: %v", fileName, err) } defer newFile.Close() @@ -136,12 +140,12 @@ func addFileToZip(zipWriter *zip.Writer, fileName string, info os.FileInfo) { archWriter, err := zipWriter.Create(fileName) if err != nil { - log.Errorf("Couldn't create archive writer for file - %v", err.Error()) + log.Fatalf("Couldn't create archive writer for file: %v", err) } if !info.IsDir() { if _, err := io.Copy(archWriter, newFile); err != nil { - log.Errorf("Couldn't write file to architve - %v", err.Error()) + log.Fatalf("Couldn't write file to architve: %v", err) } } } @@ -161,17 +165,18 @@ func getSolutionManifest(path string) (*Manifest, error) { manifestPath := fmt.Sprintf("%s/manifest.json", path) manifestFile, err := os.Open(manifestPath) if err != nil { - log.Errorf("The folder %s is not a solution package root folder", path) - return nil, err + return nil, fmt.Errorf("%q is not a solution package root folder", path) } defer manifestFile.Close() - manifestBytes, _ := io.ReadAll(manifestFile) - var manifest *Manifest + manifestBytes, err := io.ReadAll(manifestFile) + if err != nil { + return nil, err + } + var manifest *Manifest err = json.Unmarshal(manifestBytes, &manifest) if err != nil { - log.Errorf("Can't generate a manifest objects from the manifest.json, make sure your manifest.json file is correct. - %v", err.Error()) return nil, err } diff --git a/cmd/solution/push.go b/cmd/solution/push.go index bf3c0a57..a4a97c5f 100644 --- a/cmd/solution/push.go +++ b/cmd/solution/push.go @@ -33,10 +33,14 @@ import ( var solutionPushCmd = &cobra.Command{ Use: "push", Short: "Deploy your solution", - Long: `This command allows the current tenant specified in the profile to deploy a solution bundle archive into the FSO Platform. - -Usage: - fsoc solution push --solution-bundle=`, + Long: `This command allows the current tenant specified in the profile to deploy a solution to the FSO Platform. + +Examples: + fsoc solution push + fsoc solution push --solution-bundle=mysolution.zip + +The first command deploys a solution from the current directory. The second command +deploys a solution from an existing archive file.`, Args: cobra.ExactArgs(0), Run: pushSolution, TraverseChildren: true, @@ -44,8 +48,7 @@ Usage: func getSolutionPushCmd() *cobra.Command { solutionPushCmd.Flags(). - String("solution-bundle", "", "The fully qualified path name for the solution bundle .zip file") - //_ = solutionPushCmd.MarkFlagRequired("solution-package") + String("solution-bundle", "", "fully qualified path name for the solution bundle .zip file") return solutionPushCmd @@ -58,7 +61,7 @@ func pushSolution(cmd *cobra.Command, args []string) { if solutionBundlePath == "" { currentDir, err := os.Getwd() if err != nil { - log.Fatal("Please use solution-bundle flag or run this command in a folder with a solution") + log.Fatal("Please run this command in a folder with a solution or use the --solution-bundle flag") } manifestPath = currentDir if !isSolutionPackageRoot(manifestPath) { @@ -84,7 +87,7 @@ func pushSolution(cmd *cobra.Command, args []string) { file, err := os.Open(solutionArchivePath) if err != nil { - log.Fatalf("Failed to open file %s - %v", solutionArchivePath, err.Error()) + log.Fatalf("Failed to open file %q: %v", solutionArchivePath, err) } defer file.Close() @@ -93,12 +96,12 @@ func pushSolution(cmd *cobra.Command, args []string) { fw, err := writer.CreateFormFile("file", solutionArchivePath) if err != nil { - log.Fatalf("Failed to create form file - %v", err.Error()) + log.Fatalf("Failed to create form file: %v", err) } _, err = io.Copy(fw, file) if err != nil { - log.Errorf("Failed to copy file %s into file writer - %v", solutionArchivePath, err.Error()) + log.Fatalf("Failed to copy file %q into file writer: %v", solutionArchivePath, err) } writer.Close() @@ -111,15 +114,15 @@ func pushSolution(cmd *cobra.Command, args []string) { var res any - output.PrintCmdStatus(cmd, message) + output.PrintCmdStatus(cmd, fmt.Sprintf("%v\n", message)) err = api.HTTPPost(getSolutionPushUrl(), body.Bytes(), &res, &api.Options{Headers: headers}) if err != nil { - log.Fatalf("Solution command failed: %v", err.Error()) + log.Fatalf("Solution command failed: %v", err) } // message = fmt.Sprintf("Solution %s - %s was successfully deployed.", manifest.Name, manifest.SolutionVersion) - message = fmt.Sprintf("Solution bundle %s was successfully deployed.\n", solutionArchivePath) + message = fmt.Sprintf("Solution bundle %q was successfully deployed.\n", solutionArchivePath) output.PrintCmdStatus(cmd, message) } @@ -134,26 +137,26 @@ func generateZipNoCmd(sltnPackagePath string) *os.File { archiveFileName := fmt.Sprintf("%s.zip", solutionName) archive, err := os.Create(archiveFileName) if err != nil { - panic(err) + log.Fatalf("Failed to create a bundle archive %q: %v", archiveFileName, err) } defer archive.Close() zipWriter := zip.NewWriter(archive) fsocWorkingDir, err := os.Getwd() if err != nil { - log.Errorf("Couldn't read fsoc working directory: %v", err) + log.Fatalf("Couldn't read the working directory: %v", err) } solutionRootFolder := filepath.Dir(sltnPackagePath) err = os.Chdir(solutionRootFolder) if err != nil { - log.Errorf("Couldn't switch working folder to solution package folder: %v", err) + log.Fatalf("Couldn't switch working folder to solution package folder: %v", err) } defer func() { err := os.Chdir(fsocWorkingDir) if err != nil { - log.Errorf("Couldn't switch working folder back to fsoc working folder: %v", err) + log.Fatalf("Couldn't switch working folder back to the original one: %v", err) } }() @@ -166,7 +169,7 @@ func generateZipNoCmd(sltnPackagePath string) *os.File { return nil }) if err != nil { - log.Errorf("Error traversing the folder: %v", err.Error()) + log.Fatalf("Error traversing the solution folder: %v", err) } zipWriter.Close() diff --git a/cmd/solution/status.go b/cmd/solution/status.go index 506aad8c..604f5818 100644 --- a/cmd/solution/status.go +++ b/cmd/solution/status.go @@ -48,14 +48,9 @@ var solutionStatusCmd = &cobra.Command{ Short: "Get the installation/upload status of a solution", Long: `This command provides the ability to see the current installation and upload status of a solution. - Usage: - fsoc solution status --name --solution-version --status-type [upload | install | all] - - Flags/Options: - --name - Flag to indicate the name of the solution for which you would like to fetch the upload/installation status - --solution-version - OPTIONAL Flag to indicate the version of the solution for which you would like to fetch the upload/installation status - --status-type - OPTIONAL Flag to specify the status that you would like to view. If not specified, the output will contain both solution upload and solution installation status information - `, +Example: + fsoc solution status --name spacefleet --status-type=all +`, RunE: func(cmd *cobra.Command, args []string) error { return getSolutionStatus(cmd, args) }, @@ -67,6 +62,7 @@ func getSolutionStatusCmd() *cobra.Command { solutionStatusCmd.Flags(). String("name", "", "The name of the solution for which you would like to retrieve the upload status") _ = solutionStatusCmd.MarkFlagRequired("name") + solutionStatusCmd.Flags(). String("solution-version", "", "The version of the solution for which you would like to retrieve the upload status") solutionStatusCmd.Flags(). @@ -82,7 +78,7 @@ func getObject(url string, headers map[string]string) StatusItem { err := api.HTTPGet(url, &res, &api.Options{Headers: headers}) if err != nil { - log.Fatalf("Issue fetching install/upload object: %v", err) + log.Fatalf("Error fetching status object %q: %v", url, err) } if len(res.Items) > 0 { diff --git a/cmd/solution/subscribe.go b/cmd/solution/subscribe.go index c1e03525..198bf603 100644 --- a/cmd/solution/subscribe.go +++ b/cmd/solution/subscribe.go @@ -34,8 +34,8 @@ var solutionSubscribeCmd = &cobra.Command{ Short: "Subscribe to a solution", Long: `This command allows the current tenant specified in the profile to subscribe to a solution. -Usage: - fsoc solution subscribe --name=`, +Example: + fsoc solution subscribe --name=spacefleet`, Args: cobra.ExactArgs(0), Run: subscribeToSolution, TraverseChildren: true, @@ -53,7 +53,7 @@ func getSubscribeSolutionCmd() *cobra.Command { func manageSubscription(cmd *cobra.Command, args []string, isSubscribed bool) { solutionName, _ := cmd.Flags().GetString("name") if solutionName == "" { - log.Fatal("Solution name cannot be empty, use --name=SOLUTION") + log.Fatal("Solution name cannot be empty, use --name=") } var message string @@ -79,8 +79,7 @@ func manageSubscription(cmd *cobra.Command, args []string, isSubscribed bool) { var res any err := api.JSONPatch(getSolutionSubscribeUrl()+"/"+solutionName, &subscribe, &res, &api.Options{Headers: headers}) if err != nil { - log.Errorf("Solution command failed: %v", err.Error()) - return + log.Fatalf("Solution command failed: %v", err) } if isSubscribed { diff --git a/cmd/solution/unsubscribe.go b/cmd/solution/unsubscribe.go index 662a6202..3917eb66 100644 --- a/cmd/solution/unsubscribe.go +++ b/cmd/solution/unsubscribe.go @@ -29,8 +29,8 @@ var solutionUnsubscribeCmd = &cobra.Command{ Short: "Unsubscribe from a solution", Long: `This command allows the current tenant specified in the profile to unsubscribe from a solution. -Usage: - fsoc solution unsubscribe --name=`, +Example: + fsoc solution unsubscribe --name=spacefleet`, Args: cobra.ExactArgs(0), Run: unsubscribeFromSolution, TraverseChildren: true, @@ -48,13 +48,12 @@ func getUnsubscribeSolutionCmd() *cobra.Command { func unsubscribeFromSolution(cmd *cobra.Command, args []string) { solutionName, _ := cmd.Flags().GetString("name") if solutionName == "" { - log.Fatal("Solution name cannot be empty, use --name=SOLUTION") + log.Fatal("Solution name cannot be empty, use --name=") } isSystemSolution, err := isSystemSolution(solutionName) if err != nil { - log.Fatalf("Failed to check solution status: %v", err.Error()) - return + log.Fatalf("Failed to get solution status: %v", err) } if isSystemSolution { log.Fatalf("Cannot unsubscribe tenant from solution %s because it is a system solution\n", solutionName) diff --git a/cmd/solution/validate.go b/cmd/solution/validate.go index 9cf5208e..acf7124f 100644 --- a/cmd/solution/validate.go +++ b/cmd/solution/validate.go @@ -60,8 +60,8 @@ var solutionValidateCmd = &cobra.Command{ Short: "Validate your solution package", Long: `This command allows the current tenant specified in the profile to upload the specified solution bundle for the purpose of validating its contents -Usage: - fsoc solution validate --solution-bundle=`, +Example: + fsoc solution validate --solution-bundle=mysolution.zip`, Args: cobra.ExactArgs(0), Run: validateSolution, TraverseChildren: true, @@ -79,7 +79,7 @@ func validateSolution(cmd *cobra.Command, args []string) { file, err := os.Open(solutionArchivePath) if err != nil { - log.Fatalf("Failed to open file %s - %v", solutionArchivePath, err.Error()) + log.Fatalf("Failed to open file %q: %v", solutionArchivePath, err) } defer file.Close() @@ -88,13 +88,13 @@ func validateSolution(cmd *cobra.Command, args []string) { fw, err := writer.CreateFormFile("file", solutionArchivePath) if err != nil { - log.Fatalf("Failed to create form file - %v", err.Error()) + log.Fatalf("Failed to create form file: %v", err) } _, err = io.Copy(fw, file) if err != nil { writer.Close() - log.Fatalf("Failed to copy file %s into file writer - %v", solutionArchivePath, err.Error()) + log.Fatalf("Failed to copy file %q into file writer: %v", solutionArchivePath, err) } writer.Close() @@ -108,13 +108,12 @@ func validateSolution(cmd *cobra.Command, args []string) { var res Result err = api.HTTPPost(getSolutionValidateUrl(), body.Bytes(), &res, &api.Options{Headers: headers}) - if err != nil { - log.Fatalf("Solution validate command failed: %v", err.Error()) + log.Fatalf("Solution validate command failed: %v", err) } if res.Valid { - message = fmt.Sprintf("Solution bundle %s validated successfully\n", solutionArchivePath) + message = fmt.Sprintf("Solution bundle %s validated successfully.\n", solutionArchivePath) } else { message = getSolutionValidationErrorsString(res.Errors.Total, res.Errors) } @@ -124,7 +123,7 @@ func validateSolution(cmd *cobra.Command, args []string) { func getSolutionValidationErrorsString(total int, errors Errors) string { var message = fmt.Sprintf("\n%d errors detected while validating solution package\n", total) for _, err := range errors.Items { - message += fmt.Sprintf("Error Content: %+v \n", err) + message += fmt.Sprintf("- Error Content: %+v\n", err) } message += "\n"