From c5fac7bbecde46df7030076831c78a91f8d8cc43 Mon Sep 17 00:00:00 2001 From: Malte Bauch <malte.bauch@stud.h-da.de> Date: Wed, 6 Sep 2023 09:06:16 +0000 Subject: [PATCH] Resolve "PostRun function in cli does not get called if an error is thrown" Update all CLI commands to use `Run` (no error returning) instead of `RunE` (run with returning errors), since we already had an error logging through `pterm`. Prompt mode does check now for suggestions if the last cached suggestion is the right one for the current command. See merge request danet/gosdn!540 --- cli/cmd/changeCommit.go | 2 + cli/cmd/changeConfirm.go | 2 + cli/cmd/changeGet.go | 1 + cli/cmd/changeList.go | 11 ++- cli/cmd/list.go | 10 +-- cli/cmd/login.go | 9 ++- cli/cmd/logout.go | 8 +-- cli/cmd/networkElementCreate.go | 10 ++- cli/cmd/networkElementList.go | 8 +-- cli/cmd/networkElementPathDelete.go | 9 ++- cli/cmd/networkElementPathGet.go | 7 +- cli/cmd/networkElementPathGetIntended.go | 8 +-- cli/cmd/networkElementPathSet.go | 20 +++--- cli/cmd/networkElementRemove.go | 7 +- cli/cmd/networkElementShow.go | 6 +- cli/cmd/networkElementSubscribe.go | 8 +-- cli/cmd/pluginList.go | 8 +-- cli/cmd/pndCreate.go | 6 +- cli/cmd/pndGet.go | 5 +- cli/cmd/pndList.go | 8 +-- cli/cmd/pndRemove.go | 7 +- cli/cmd/pndUse.go | 9 ++- cli/cmd/prompt.go | 87 +++++++++++++++++------- cli/cmd/userCreate.go | 8 +-- cli/cmd/userDelete.go | 8 +-- cli/cmd/userGet.go | 8 +-- cli/cmd/userGetAll.go | 8 +-- cli/cmd/userUpdate.go | 11 +-- go.sum | 4 -- 29 files changed, 159 insertions(+), 144 deletions(-) diff --git a/cli/cmd/changeCommit.go b/cli/cmd/changeCommit.go index 49e035eed..406da9e9c 100644 --- a/cli/cmd/changeCommit.go +++ b/cli/cmd/changeCommit.go @@ -49,10 +49,12 @@ Change UUID must be specified as positional argument.`, cuid, err := uuid.Parse(args[0]) if err != nil { pterm.Error.Println(err) + return } resp, err := pndAdapter.Commit(createContextWithAuthorization(), cuid) if err != nil { pterm.Error.Println(err) + return } for _, r := range resp.GetResponses() { pterm.Info.Printfln("Change with ID: %s has been committed.", r.GetId()) diff --git a/cli/cmd/changeConfirm.go b/cli/cmd/changeConfirm.go index 249738c6e..41550ede0 100644 --- a/cli/cmd/changeConfirm.go +++ b/cli/cmd/changeConfirm.go @@ -49,11 +49,13 @@ Change UUID must be specified as positional argument`, cuid, err := uuid.Parse(args[0]) if err != nil { pterm.Error.Println(err) + return } resp, err := pndAdapter.Confirm(createContextWithAuthorization(), cuid) if err != nil { pterm.Error.Println(err) + return } for _, r := range resp.GetResponses() { pterm.Info.Printfln("Change with ID: %s has been confirmed.", r.GetId()) diff --git a/cli/cmd/changeGet.go b/cli/cmd/changeGet.go index a7c1aa331..ff8e3c3b4 100644 --- a/cli/cmd/changeGet.go +++ b/cli/cmd/changeGet.go @@ -49,6 +49,7 @@ var getCmd = &cobra.Command{ changes, err := pndAdapter.GetChange(createContextWithAuthorization(), args[0]) if err != nil { pterm.Error.Println(err) + return } for _, c := range changes.GetChange() { title := pterm.Sprintf("Change with ID: %s", c.GetId()) diff --git a/cli/cmd/changeList.go b/cli/cmd/changeList.go index d5b0ea11e..2b1a57501 100644 --- a/cli/cmd/changeList.go +++ b/cli/cmd/changeList.go @@ -43,17 +43,17 @@ var changeListCmd = &cobra.Command{ Short: "list all changes", Long: `Lists all configuration changes with their UUIDs.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Process change list request") committed, err := pndAdapter.CommittedChanges(createContextWithAuthorization()) if err != nil { spinner.Fail(err) - return err + return } pending, err := pndAdapter.PendingChanges(createContextWithAuthorization()) if err != nil { spinner.Fail(err) - return err + return } // TODO: maybe we want to return more information about changes? E.g., @@ -70,10 +70,9 @@ var changeListCmd = &cobra.Command{ spinner.Success() err = pterm.DefaultTable.WithHasHeader().WithData(data).Render() if err != nil { - return err + pterm.Error.Println(err) + return } - - return nil }, } diff --git a/cli/cmd/list.go b/cli/cmd/list.go index 7ff31bcbf..2e476d4e7 100644 --- a/cli/cmd/list.go +++ b/cli/cmd/list.go @@ -33,6 +33,7 @@ package cmd import ( "code.fbi.h-da.de/danet/gosdn/controller/api" + "github.com/pterm/pterm" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -44,16 +45,18 @@ var listCmd = &cobra.Command{ Short: "List all PNDs, SBIs and MNEs on the controller", Long: `List all PNDs, SBIs and MNEs on the controller.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { addr := viper.GetString("controllerApiEndpoint") resp, err := api.GetIds(createContextWithAuthorization(), addr) if err != nil { - return err + pterm.Error.Println(err) + return } for i, pnd := range resp { mneResp, err := api.GetFlattenedNetworkElements(createContextWithAuthorization(), addr, pnd.GetId()) if err != nil { - return err + pterm.Error.Println(err) + return } log.Infof("PND %v: name: %v, uuid: %v", i+1, pnd.Name, pnd.Id) for k, mne := range mneResp.GetMne() { @@ -61,7 +64,6 @@ var listCmd = &cobra.Command{ log.Infof(" Plugin %v: uuid: %v", k+1, mne.GetPid()) } } - return nil }, } diff --git a/cli/cmd/login.go b/cli/cmd/login.go index ac210be64..babf01ee8 100644 --- a/cli/cmd/login.go +++ b/cli/cmd/login.go @@ -47,12 +47,13 @@ var loginCmd = &cobra.Command{ Long: `Logs the user in to allow further actions on the controller. User credentials need to be provided in the body`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Login attempt for user: ", nbUserName) if controllerAPIEndpoint != "" { if err := viper.WriteConfig(); err != nil { pterm.Error.Println("Could not write config:", err) + return } pterm.Info.Println("New controller address: ", viper.GetString("controllerAPIEndpoint")) } @@ -66,7 +67,7 @@ var loginCmd = &cobra.Command{ resp, err := api.Login(context.TODO(), viper.GetString("controllerAPIEndpoint"), nbUserName, nbUserPwd) if err != nil { spinner.Fail("Login failed: ", err) - return err + return } spinner.Success("Authentication for ", nbUserName, " was successful.") @@ -78,12 +79,10 @@ var loginCmd = &cobra.Command{ err = viper.WriteConfig() if err != nil { pterm.Error.Println(err) - return err + return } pterm.Info.Println("Authentication-User-Token:", userToken) - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/logout.go b/cli/cmd/logout.go index 997b035aa..68bc8a739 100644 --- a/cli/cmd/logout.go +++ b/cli/cmd/logout.go @@ -46,13 +46,13 @@ var logoutCmd = &cobra.Command{ Short: "Logs the current user out", Long: `Logs the current user out. Further actions on the controller are not permitted after this.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Logout attempt for user: ", nbUserName) resp, err := api.Logout(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), nbUserName) if err != nil { spinner.Fail("Logout failed: ", err) - return err + return } userToken = "" @@ -61,12 +61,10 @@ var logoutCmd = &cobra.Command{ err = viper.WriteConfig() if err != nil { pterm.Error.Println(err) - return err + return } spinner.Success("User ", nbUserName, " successfully logged out at ", time.Unix((resp.Timestamp/1000000000), 0), ".") - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/networkElementCreate.go b/cli/cmd/networkElementCreate.go index 4cc764922..e2ed6cb9d 100644 --- a/cli/cmd/networkElementCreate.go +++ b/cli/cmd/networkElementCreate.go @@ -47,12 +47,12 @@ var networkElementCreateCmd = &cobra.Command{ Network element address must be provided with IP and port,e.g., 192.168.1.1:6030. User credentials need to be provided as parameters if they diverge from the default credentials (user:'admin' and pw:'arista').`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Creating new network element") err := checkIPPort(address) if err != nil { spinner.Fail(err) - return err + return } opt := &tpb.TransportOption{ @@ -68,20 +68,18 @@ if they diverge from the default credentials (user:'admin' and pw:'arista').`, pluginUUID, err := uuid.Parse(pluginID) if err != nil { spinner.Fail(err) - return err + return } resp, err := pndAdapter.AddNetworkElement(createContextWithAuthorization(), mneName, opt, pluginUUID) if err != nil { spinner.Fail(err) - return err + return } for _, r := range resp.GetResponses() { spinner.Success("Network element has been created with ID: ", r.GetId()) } - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/networkElementList.go b/cli/cmd/networkElementList.go index d61c2ea78..be6ce028a 100644 --- a/cli/cmd/networkElementList.go +++ b/cli/cmd/networkElementList.go @@ -44,13 +44,13 @@ var networkElementListCmd = &cobra.Command{ Short: "list all network elements in current PND", Long: "List all orchestrated network network elements within the current PND.", - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Fetching data from controller") resp, err := pndAdapter.GetFlattenedNetworkElements(createContextWithAuthorization()) if err != nil { spinner.Fail(err) - return err + return } data := pterm.TableData{[]string{"UUID", "Name", "Plugin-UUID"}} @@ -62,10 +62,8 @@ var networkElementListCmd = &cobra.Command{ err = pterm.DefaultTable.WithHasHeader().WithData(data).Render() if err != nil { - return err + return } - - return nil }, } diff --git a/cli/cmd/networkElementPathDelete.go b/cli/cmd/networkElementPathDelete.go index 51f1c9f34..b40bdccc1 100644 --- a/cli/cmd/networkElementPathDelete.go +++ b/cli/cmd/networkElementPathDelete.go @@ -48,18 +48,18 @@ var networkElementPathDeleteCmd = &cobra.Command{ Long: `Delete a path for a given orchestrated network network element. The network element UUID and request path must be specified as a positional arguments.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Create a path deletion request.") mneid, err := uuid.Parse(args[0]) if err != nil { spinner.Fail(err) - return err + return } path, err := ygot.StringToStructuredPath(args[1]) if err != nil { spinner.Fail(err) - return err + return } resp, err := pndAdapter.ChangeMNE( @@ -71,7 +71,7 @@ The network element UUID and request path must be specified as a positional argu ) if err != nil { spinner.Fail(err) - return err + return } for _, r := range resp.Responses { @@ -81,7 +81,6 @@ The network element UUID and request path must be specified as a positional argu spinner.Fail("An error occurred while creating a path deletion request for network element with ID: ", r.GetId(), r.GetStatus()) } } - return nil }, } diff --git a/cli/cmd/networkElementPathGet.go b/cli/cmd/networkElementPathGet.go index d02fccf2e..f7ab234e4 100644 --- a/cli/cmd/networkElementPathGet.go +++ b/cli/cmd/networkElementPathGet.go @@ -47,11 +47,11 @@ var networkElementPathGetCmd = &cobra.Command{ Long: `Requests a path from a specified managed network element on the controller. The network element UUID and request path must be specified as a positional arguments.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { mneid, err := uuid.Parse(args[0]) if err != nil { pterm.Error.Println(err) - return err + return } res, err := pndAdapter.RequestPath( @@ -61,7 +61,7 @@ The network element UUID and request path must be specified as a positional argu ) if err != nil { pterm.Error.Println(err) - return err + return } for _, n := range res.MneNotification { @@ -91,7 +91,6 @@ The network element UUID and request path must be specified as a positional argu pterm.Info.Println(panels) } - return nil }, } diff --git a/cli/cmd/networkElementPathGetIntended.go b/cli/cmd/networkElementPathGetIntended.go index 1e7e3c3a4..ceb8f50ed 100644 --- a/cli/cmd/networkElementPathGetIntended.go +++ b/cli/cmd/networkElementPathGetIntended.go @@ -49,11 +49,11 @@ var networkElementPathGetIntendedCmd = &cobra.Command{ Long: `Requests the intended path from a specified managed network element on the controller. The network element UUID and request path must be specified as a positional arguments.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { mneid, err := uuid.Parse(args[0]) if err != nil { pterm.Error.Println(err) - return err + return } res, err := pndAdapter.RequestIntendedPath( @@ -63,7 +63,7 @@ The network element UUID and request path must be specified as a positional argu ) if err != nil { pterm.Error.Println(err) - return err + return } var stringVal = "Update: " @@ -100,8 +100,6 @@ The network element UUID and request path must be specified as a positional argu if panel3 == "" { fmt.Println(stringVal) } - - return nil }, } diff --git a/cli/cmd/networkElementPathSet.go b/cli/cmd/networkElementPathSet.go index 6dd9e1ddc..e29cbf332 100644 --- a/cli/cmd/networkElementPathSet.go +++ b/cli/cmd/networkElementPathSet.go @@ -32,7 +32,6 @@ POSSIBILITY OF SUCH DAMAGE. package cmd import ( - "fmt" "os" mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement" @@ -58,17 +57,17 @@ only one value supported for now. The network element UUID, request path and value must be specified as positional arguments. To enable replacing behaviour (destructive!), set the --replace flag."`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Create a path set request.") //NOTE: this currently only works in prompt mode if c == nil { spinner.Fail("Currently the SET command is only supported with 'gosdnc prompt'") - return fmt.Errorf("Currently the SET command is only supported with 'gosdnc prompt'") + return } mneid, err := uuid.Parse(args[0]) if err != nil { spinner.Fail(err) - return err + return } var value string var operation mnepb.ApiOperation @@ -83,7 +82,7 @@ To enable replacing behaviour (destructive!), set the --replace flag."`, completer, ok := c.YangSchemaCompleterMap[mneid] if !ok { spinner.Fail("No schema completer found for network element ", mneid.String()) - return fmt.Errorf("No schema completer found for network element %s", mneid.String()) + return } var typedValue *gnmi.TypedValue @@ -93,11 +92,11 @@ To enable replacing behaviour (destructive!), set the --replace flag."`, typedValue, err = convertStringToGnmiTypedValue(args[2], completer.Entry.Type) if err != nil { spinner.Fail(err) - return err + return } } else { spinner.Fail("The provided path is no leaf. Please provide a file with JSON-encoded text per RFC7951. Use '--file path' for this.") - return fmt.Errorf("The provided path is no leaf. Please provide a file with JSON-encoded text per RFC7951. Use '--file path' for this.") + return } } else { @@ -105,7 +104,7 @@ To enable replacing behaviour (destructive!), set the --replace flag."`, value, err = fileContentToString(file) if err != nil { spinner.Fail(err) - return err + return } typedValue = &gnmi.TypedValue{ Value: &gnmi.TypedValue_JsonIetfVal{ @@ -117,7 +116,7 @@ To enable replacing behaviour (destructive!), set the --replace flag."`, path, err := ygot.StringToStructuredPath(args[1]) if err != nil { spinner.Fail(err) - return err + return } resp, err := pndAdapter.ChangeMNE( @@ -130,7 +129,7 @@ To enable replacing behaviour (destructive!), set the --replace flag."`, if err != nil { spinner.Fail(err) - return err + return } for _, r := range resp.GetResponses() { @@ -140,7 +139,6 @@ To enable replacing behaviour (destructive!), set the --replace flag."`, executeFunc("change confirm " + r.GetId()) } } - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/networkElementRemove.go b/cli/cmd/networkElementRemove.go index f278b6460..b31fa1445 100644 --- a/cli/cmd/networkElementRemove.go +++ b/cli/cmd/networkElementRemove.go @@ -47,20 +47,19 @@ var networkElementRemoveCmd = &cobra.Command{ Long: `Deletes an orchestrated network network element on the controller. The network element UUID must be specified as a positional argument.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Removing network element with ID: ", args[0]) mneid, err := uuid.Parse(args[0]) if err != nil { spinner.Fail(err) - return err + return } _, err = pndAdapter.RemoveNetworkElement(createContextWithAuthorization(), mneid) if err != nil { spinner.Fail(err) - return err + return } spinner.Success("Network element has been deleted, ID: ", mneid.String()) - return nil }, } diff --git a/cli/cmd/networkElementShow.go b/cli/cmd/networkElementShow.go index 71581aec8..6e150c4da 100644 --- a/cli/cmd/networkElementShow.go +++ b/cli/cmd/networkElementShow.go @@ -46,11 +46,11 @@ Network element UUID or name must be specified as a positional argument. The network element information returned is the information as currently stored in the controller. The actual network element is not queried directly.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { resp, err := pndAdapter.GetNetworkElement(createContextWithAuthorization(), args[0]) if err != nil { pterm.Error.Println(err) - return err + return } mne := resp.GetMne() @@ -64,8 +64,6 @@ The actual network element is not queried directly.`, }).Srender() pterm.DefaultBox.WithRightPadding(0).WithBottomPadding(0).Println(panels) - - return nil }, } diff --git a/cli/cmd/networkElementSubscribe.go b/cli/cmd/networkElementSubscribe.go index 245d9f6fb..5b9682818 100644 --- a/cli/cmd/networkElementSubscribe.go +++ b/cli/cmd/networkElementSubscribe.go @@ -50,11 +50,11 @@ var deviceSubscribeCmd = &cobra.Command{ Long: `Requests a subscription on paths from a specified orchestrated network device on the controller. The device UUID and requested paths must be specified as a positional arguments.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { did, err := uuid.Parse(args[0]) if err != nil { pterm.Error.Println(err) - return err + return } subClient, err := pndAdapter.SubscribeMNEPath( @@ -73,7 +73,7 @@ The device UUID and requested paths must be specified as a positional arguments. ) if err != nil { pterm.Error.Println(err) - return err + return } for i := 0; i < 5; i++ { @@ -95,8 +95,6 @@ The device UUID and requested paths must be specified as a positional arguments. pterm.Println(subscribeResponse.String()) } - - return nil }, } diff --git a/cli/cmd/pluginList.go b/cli/cmd/pluginList.go index 2a87fc246..8594f2836 100644 --- a/cli/cmd/pluginList.go +++ b/cli/cmd/pluginList.go @@ -44,12 +44,12 @@ var pluginListCmd = &cobra.Command{ Short: "List all information about available plugins.", Long: `List all information about abailable plugins.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Fetching list of available plugins from the controller.") resp, err := pndAdapter.GetAvailablePlugins(createContextWithAuthorization()) if err != nil { spinner.Fail(err) - return err + return } data := pterm.TableData{[]string{"UUID", "Name", "Author", "Firmware", "Version"}} @@ -62,10 +62,8 @@ var pluginListCmd = &cobra.Command{ err = pterm.DefaultTable.WithHasHeader().WithData(data).Render() if err != nil { - return err + return } - - return nil }, } diff --git a/cli/cmd/pndCreate.go b/cli/cmd/pndCreate.go index 5c3c83d53..dc4c6c6c6 100644 --- a/cli/cmd/pndCreate.go +++ b/cli/cmd/pndCreate.go @@ -49,12 +49,12 @@ campus building or site. A description must be passed as positional argument.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Creating new PND") resp, err := api.AddPnd(createContextWithAuthorization(), viper.GetString("controllerApiEndpoint"), pndName, pndDescription) if err != nil { spinner.Fail(err) - return err + return } if resp.Status == ppb.Status_STATUS_OK { @@ -62,8 +62,6 @@ A description must be passed as positional argument.`, } else { spinner.Fail("Failed creating the PND with name: ", pndName) } - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/pndGet.go b/cli/cmd/pndGet.go index 1c2c0a4d6..7fdabe07f 100644 --- a/cli/cmd/pndGet.go +++ b/cli/cmd/pndGet.go @@ -44,12 +44,12 @@ var pndGetCmd = &cobra.Command{ Short: "Request a PND by uuid.", Long: `Request a PND specified by uuid. The PND's UUID, Name and Description are returned.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Fetching requested PNDs from controller.") resp, err := api.GetPnd(createContextWithAuthorization(), viper.GetString("controllerApiEndpoint"), args[0]) if err != nil { spinner.Fail(err) - return err + return } pnd := resp.Pnd @@ -65,7 +65,6 @@ var pndGetCmd = &cobra.Command{ spinner.Success() pterm.DefaultBox.WithRightPadding(0).WithBottomPadding(0).Println(panels) - return nil }, } diff --git a/cli/cmd/pndList.go b/cli/cmd/pndList.go index 8e77bbb30..f6b15d422 100644 --- a/cli/cmd/pndList.go +++ b/cli/cmd/pndList.go @@ -45,12 +45,12 @@ var pndListCmd = &cobra.Command{ Short: "List all information about the current PND", Long: `List all information about the current PND.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Fetching PND list from controller") resp, err := api.GetPnds(createContextWithAuthorization(), pndAdapter.Endpoint()) if err != nil { spinner.Fail(err) - return err + return } data := pterm.TableData{[]string{"UUID", "Name", "Description"}} @@ -62,10 +62,8 @@ var pndListCmd = &cobra.Command{ err = pterm.DefaultTable.WithHasHeader().WithData(data).Render() if err != nil { - return err + return } - - return nil }, } diff --git a/cli/cmd/pndRemove.go b/cli/cmd/pndRemove.go index 9b4eafca3..3c641a817 100644 --- a/cli/cmd/pndRemove.go +++ b/cli/cmd/pndRemove.go @@ -45,20 +45,19 @@ var pndRemoveCmd = &cobra.Command{ Short: "Removes the PND with the provided ID", Long: "Removes the PND with the provided ID", - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { spinner, _ := pterm.DefaultSpinner.Start("Removing PND with ID: ", args[0]) pid, err := uuid.Parse(args[0]) if err != nil { spinner.Fail(err) - return err + return } _, err = pndAdapter.RemovePnd(createContextWithAuthorization(), pid) if err != nil { spinner.Fail(err) - return err + return } spinner.Success("PND has been deleted, ID: ", pid.String()) - return nil }, } diff --git a/cli/cmd/pndUse.go b/cli/cmd/pndUse.go index 5e849e2b3..b1c0cee7c 100644 --- a/cli/cmd/pndUse.go +++ b/cli/cmd/pndUse.go @@ -46,19 +46,19 @@ var pndUseCmd = &cobra.Command{ Short: "change currently used PND for the CLI", Long: `Change currently used PND for the CLI.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { var newPND = args[0] newPndAdapter, err := adapter.NewPndAdapter(newPND, viper.GetString("controllerAPIEndpoint")) if err != nil { pterm.Error.Println(err) - return err + return } _, err = api.GetPnd(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), newPND) if err != nil { pterm.Error.Println(err) - return err + return } viper.Set("CLI_PND", newPND) @@ -66,11 +66,10 @@ var pndUseCmd = &cobra.Command{ err = viper.WriteConfig() if err != nil { pterm.Error.Println(err) - return err + return } pndAdapter = newPndAdapter pterm.Info.Printf("PND with ID: %s has been set for usage.\n", pndAdapter.ID().String()) - return nil }, } diff --git a/cli/cmd/prompt.go b/cli/cmd/prompt.go index af3559c09..f297fe392 100644 --- a/cli/cmd/prompt.go +++ b/cli/cmd/prompt.go @@ -51,10 +51,17 @@ import ( var c *PromptCompleter +// suggestionTracker is used to keep track of the last used command in +// combination with the resulting suggestions. +type suggestionTracker struct { + lastCommand *cobra.Command + lastSuggestion []prompt.Suggest +} + // PromptCompleter provides completion for a Network Element. type PromptCompleter struct { YangSchemaCompleterMap map[uuid.UUID]*completer.YangSchemaCompleter - currentSuggestions []prompt.Suggest + lastSuggestions *suggestionTracker // nolint:unused document *prompt.Document history []string @@ -137,14 +144,14 @@ func filterFlagSlice(input []string) (commandSlice []string, flagSlice []string) return commandSlice, flagSlice } -func networkElementPathCompletion(c *PromptCompleter, d prompt.Document, inputSplit []string) []prompt.Suggest { +func networkElementPathCompletion(c *PromptCompleter, command *cobra.Command, d prompt.Document, inputSplit []string) []prompt.Suggest { switch inputLen := len(inputSplit); inputLen { case 3: - return c.updateSuggestionsThroughFunc(d, getNetworkElements) + return c.updateSuggestionsThroughFunc(d, command, getNetworkElements) case 4: id, err := uuid.Parse(inputSplit[inputLen-1]) if err != nil { - return c.updateSuggestionsThroughFunc(d, getNetworkElements) + return c.updateSuggestionsThroughFunc(d, command, getNetworkElements) } if c, ok := c.YangSchemaCompleterMap[id]; ok { return c.Complete(d) @@ -161,7 +168,7 @@ func networkElementPathCompletion(c *PromptCompleter, d prompt.Document, inputSp if d.GetWordBeforeCursor() == "" || d.GetWordAfterCursor() != "" { id, err := uuid.Parse(inputSplit[inputLen-2]) if err != nil { - return c.updateSuggestionsThroughFunc(d, getNetworkElements) + return c.updateSuggestionsThroughFunc(d, command, getNetworkElements) } if yc, ok := c.YangSchemaCompleterMap[id]; ok { err := yc.UpdateEntry(inputSplit[inputLen-1]) @@ -173,7 +180,7 @@ func networkElementPathCompletion(c *PromptCompleter, d prompt.Document, inputSp } id, err := uuid.Parse(inputSplit[inputLen-2]) if err != nil { - return c.updateSuggestionsThroughFunc(d, getNetworkElements) + return c.updateSuggestionsThroughFunc(d, command, getNetworkElements) } if yc, ok := c.YangSchemaCompleterMap[id]; ok { return yc.Complete(d) @@ -184,15 +191,19 @@ func networkElementPathCompletion(c *PromptCompleter, d prompt.Document, inputSp return []prompt.Suggest{} } -func (pc *PromptCompleter) updateSuggestionsThroughFunc(d prompt.Document, fn func() ([]prompt.Suggest, error)) []prompt.Suggest { - if pc.currentSuggestions == nil { +func (pc *PromptCompleter) updateSuggestionsThroughFunc(d prompt.Document, c *cobra.Command, fn func() ([]prompt.Suggest, error)) []prompt.Suggest { + if pc.lastSuggestions == nil { var err error - pc.currentSuggestions, err = fn() + newSuggestions, err := fn() + pc.lastSuggestions = &suggestionTracker{ + lastCommand: c, + lastSuggestion: newSuggestions, + } if err != nil { - return prompt.FilterHasPrefix(pc.currentSuggestions, d.GetWordBeforeCursor(), true) + return prompt.FilterHasPrefix(pc.lastSuggestions.lastSuggestion, d.GetWordBeforeCursor(), true) } } - return prompt.FilterHasPrefix(pc.currentSuggestions, d.GetWordBeforeCursor(), true) + return prompt.FilterHasPrefix(pc.lastSuggestions.lastSuggestion, d.GetWordBeforeCursor(), true) } func cobraCommandCompletion(currCmd *cobra.Command, d prompt.Document, inputFlags []string, loaded []prompt.Suggest) []prompt.Suggest { @@ -218,27 +229,53 @@ func completionBasedOnCmd(c *PromptCompleter, cmd *cobra.Command, inputSplit []s switch cmd { case pndUseCmd, pndGetCmd, pndRemoveCmd: if len(inputSplitFiltered) < 3 || (len(inputSplitFiltered) == 3 && d.GetWordBeforeCursor() != "") { - suggestions := c.updateSuggestionsThroughFunc(d, getPnds) + if c.lastSuggestions != nil { + lastCommand := c.lastSuggestions.lastCommand + if lastCommand != pndUseCmd && lastCommand != pndGetCmd && lastCommand != pndRemoveCmd { + c.lastSuggestions = nil + } + } + suggestions := c.updateSuggestionsThroughFunc(d, cmd, getPnds) return cobraCommandCompletion(cmd, d, inputFlags, suggestions) } case commitCmd: + if c.lastSuggestions != nil { + if c.lastSuggestions.lastCommand != commitCmd { + c.lastSuggestions = nil + } + } if len(inputSplitFiltered) < 3 || (len(inputSplitFiltered) == 3 && d.GetWordBeforeCursor() != "") { - return c.updateSuggestionsThroughFunc(d, getPendingChanges) + return c.updateSuggestionsThroughFunc(d, cmd, getPendingChanges) } case confirmCmd: + if c.lastSuggestions != nil { + if c.lastSuggestions.lastCommand != confirmCmd { + c.lastSuggestions = nil + } + } if len(inputSplitFiltered) < 3 || (len(inputSplitFiltered) == 3 && d.GetWordBeforeCursor() != "") { - return c.updateSuggestionsThroughFunc(d, getCommittedChanges) + return c.updateSuggestionsThroughFunc(d, cmd, getCommittedChanges) } case networkElementRemoveCmd: + if c.lastSuggestions != nil { + if c.lastSuggestions.lastCommand != networkElementRemoveCmd { + c.lastSuggestions = nil + } + } if len(inputSplitFiltered) < 3 || (len(inputSplitFiltered) == 3 && d.GetWordBeforeCursor() != "") { - return c.updateSuggestionsThroughFunc(d, getNetworkElements) + return c.updateSuggestionsThroughFunc(d, cmd, getNetworkElements) } case networkElementCreateCmd: + if c.lastSuggestions != nil { + if c.lastSuggestions.lastCommand != networkElementCreateCmd { + c.lastSuggestions = nil + } + } if len(inputFlags) != 0 { if inputFlags[len(inputFlags)-1] == "--plugin-id" && ((inputSplit[len(inputSplit)-1] == "--plugin-id" && d.GetWordBeforeCursor() == "") || (inputSplit[len(inputSplit)-2] == "--plugin-id" && d.GetWordBeforeCursor() != "")) { - return c.updateSuggestionsThroughFunc(d, getAvailablePlugins) + return c.updateSuggestionsThroughFunc(d, cmd, getAvailablePlugins) } else { return cobraCommandCompletion(cmd, d, inputFlags, []prompt.Suggest{}) } @@ -246,7 +283,13 @@ func completionBasedOnCmd(c *PromptCompleter, cmd *cobra.Command, inputSplit []s return cobraCommandCompletion(cmd, d, inputFlags, []prompt.Suggest{}) } case networkElementPathGetCmd, networkElementPathGetIntendedCmd, networkElementPathSetCmd: - return networkElementPathCompletion(c, d, inputSplitFiltered) + if c.lastSuggestions != nil { + lastCommand := c.lastSuggestions.lastCommand + if lastCommand != networkElementPathGetCmd && lastCommand != networkElementPathGetIntendedCmd && lastCommand != networkElementPathSetCmd { + c.lastSuggestions = nil + } + } + return networkElementPathCompletion(c, cmd, d, inputSplitFiltered) case networkElementShowCmd: networkElements, err := getNetworkElements() if err != nil { @@ -254,7 +297,7 @@ func completionBasedOnCmd(c *PromptCompleter, cmd *cobra.Command, inputSplit []s } return networkElements case networkElementCmd, pndCmd, changeCmd: - c.currentSuggestions = nil + c.lastSuggestions = nil return cobraCommandCompletion(cmd, d, inputFlags, []prompt.Suggest{}) default: return cobraCommandCompletion(cmd, d, inputFlags, []prompt.Suggest{}) @@ -387,7 +430,7 @@ var exitCmd = &cobra.Command{ Short: "The exit command exits the interactive prompt mode.", Long: `The exit command exits the interactive prompt mode.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { rawModeOff := exec.Command("/bin/stty", "-raw", "echo") rawModeOff.Stdin = os.Stdin @@ -396,9 +439,7 @@ var exitCmd = &cobra.Command{ logrus.Error(err) os.Exit(1) } - os.Exit(0) - return nil }, } @@ -408,11 +449,9 @@ var promptCmd = &cobra.Command{ Long: `The prompt command rund the CLI in an interactive shell and provides the user with autocompletion and more...`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { c = NewPromptCompleter() c.Run() - - return nil }, } diff --git a/cli/cmd/userCreate.go b/cli/cmd/userCreate.go index f233acaf8..8a07b75ce 100644 --- a/cli/cmd/userCreate.go +++ b/cli/cmd/userCreate.go @@ -34,6 +34,7 @@ package cmd import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" "code.fbi.h-da.de/danet/gosdn/controller/api" + "github.com/pterm/pterm" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -47,7 +48,7 @@ var userCreateCmd = &cobra.Command{ User name and password hashed with (add hash method here!) required, role is optional but needed to operate on PNDs.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { roles := map[string]string{} // only active pnd for now, add option for additional param later @@ -64,11 +65,10 @@ var userCreateCmd = &cobra.Command{ resp, err := api.CreateUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), users) if err != nil { - return err + pterm.Error.Println(err) + return } log.Infof("Users created: %v", resp.Status) - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/userDelete.go b/cli/cmd/userDelete.go index 160ed5f49..aa9fdc610 100644 --- a/cli/cmd/userDelete.go +++ b/cli/cmd/userDelete.go @@ -33,6 +33,7 @@ package cmd import ( "code.fbi.h-da.de/danet/gosdn/controller/api" + "github.com/pterm/pterm" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -45,18 +46,17 @@ var userDeleteCmd = &cobra.Command{ Long: `Deletes a user with provided data. Requires the user name of the user which should be deleted.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { // only one user for now, add more later if needed users := []string{nbUserName} resp, err := api.DeleteUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), users) if err != nil { - return err + pterm.Error.Println(err) + return } log.Infof("Users deleted: %v", resp.Status) - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/userGet.go b/cli/cmd/userGet.go index f235141dc..357966913 100644 --- a/cli/cmd/userGet.go +++ b/cli/cmd/userGet.go @@ -34,6 +34,7 @@ package cmd import ( "code.fbi.h-da.de/danet/gosdn/controller/api" "github.com/google/uuid" + "github.com/pterm/pterm" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -45,7 +46,7 @@ var userGetCmd = &cobra.Command{ Short: "Requests one user", Long: `Requests one user using the provided name to search for it in the stored users.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { resp, err := api.GetUser( createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), @@ -53,15 +54,14 @@ var userGetCmd = &cobra.Command{ uuid.Nil, ) if err != nil { - return err + pterm.Error.Println(err) + return } log.Infof("ID: %v, Name: %v \n", resp.User.Id, resp.User.Name) for key, elem := range resp.User.Roles { log.Infof("Role on PND: %v %v \n", key, elem) } - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/cli/cmd/userGetAll.go b/cli/cmd/userGetAll.go index cc9fa0973..8a5c650f0 100644 --- a/cli/cmd/userGetAll.go +++ b/cli/cmd/userGetAll.go @@ -33,6 +33,7 @@ package cmd import ( "code.fbi.h-da.de/danet/gosdn/controller/api" + "github.com/pterm/pterm" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -44,10 +45,11 @@ var userGetAllCmd = &cobra.Command{ Short: "Requests all the available users", Long: `Requests all the available users.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { resp, err := api.GetAllUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint")) if err != nil { - return err + pterm.Error.Println(err) + return } for i, u := range resp.User { @@ -56,8 +58,6 @@ var userGetAllCmd = &cobra.Command{ log.Infof("Role on PND: %v %v \n", key, elem) } } - - return nil }, } diff --git a/cli/cmd/userUpdate.go b/cli/cmd/userUpdate.go index 23c62b321..7849d5835 100644 --- a/cli/cmd/userUpdate.go +++ b/cli/cmd/userUpdate.go @@ -35,6 +35,7 @@ import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" "code.fbi.h-da.de/danet/gosdn/controller/api" "github.com/google/uuid" + "github.com/pterm/pterm" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -48,7 +49,7 @@ var userUpdateCmd = &cobra.Command{ User name and password hashed with (add hash method here!) required, role is optional but needed to operate on PNDs.`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { existingUser, err := api.GetUser( createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), @@ -56,7 +57,8 @@ var userUpdateCmd = &cobra.Command{ uuid.Nil, ) if err != nil { - return err + pterm.Error.Println(err) + return } roles := map[string]string{} @@ -76,11 +78,10 @@ var userUpdateCmd = &cobra.Command{ resp, err := api.UpdateUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), users) if err != nil { - return err + pterm.Error.Println(err) + return } log.Infof("Users updated: %v", resp.Status) - - return nil }, PostRun: func(cmd *cobra.Command, args []string) { // Necessary for prompt mode. The flag variables have to be resetted, diff --git a/go.sum b/go.sum index 30bfd8d30..c68dd12cd 100644 --- a/go.sum +++ b/go.sum @@ -547,8 +547,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.0 h1:Rme6CE1aUTyV9WmrEPyGf1V+7W3iQzZ1DZkKnT6z9B0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.0/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -882,8 +880,6 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= -github.com/pterm/pterm v0.12.66 h1:bjsoMyUstaarzJ1NG7+1HGT7afR0JVMYsR3ooPeh4bo= -github.com/pterm/pterm v0.12.66/go.mod h1:nFuT9ZVkkCi8o4L1dtWuYPwDQxggLh4C263qG5nTLpQ= github.com/pterm/pterm v0.12.67 h1:5iB7ajIQROYfxYD7+sFJ4+KJhFJ+xn7QOVBm4s6RUF0= github.com/pterm/pterm v0.12.67/go.mod h1:nFuT9ZVkkCi8o4L1dtWuYPwDQxggLh4C263qG5nTLpQ= github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA= -- GitLab