github.com/randomtask1155/cli@v6.41.1-0.20181227003417-a98eed78cbde+incompatible/main.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"code.cloudfoundry.org/cli/cf/cmd"
    11  	"code.cloudfoundry.org/cli/command"
    12  	"code.cloudfoundry.org/cli/command/common"
    13  	"code.cloudfoundry.org/cli/command/flag"
    14  	"code.cloudfoundry.org/cli/command/translatableerror"
    15  	"code.cloudfoundry.org/cli/util/configv3"
    16  	"code.cloudfoundry.org/cli/util/panichandler"
    17  	"code.cloudfoundry.org/cli/util/ui"
    18  	"github.com/jessevdk/go-flags"
    19  	log "github.com/sirupsen/logrus"
    20  	"golang.org/x/crypto/ssh"
    21  )
    22  
    23  type UI interface {
    24  	DisplayError(err error)
    25  	DisplayWarning(template string, templateValues ...map[string]interface{})
    26  	DisplayText(template string, templateValues ...map[string]interface{})
    27  }
    28  
    29  type DisplayUsage interface {
    30  	DisplayUsage()
    31  }
    32  
    33  type TriggerLegacyMain interface {
    34  	LegacyMain()
    35  	error
    36  }
    37  
    38  const switchToV2 = -3
    39  
    40  var ErrFailed = errors.New("command failed")
    41  var ParseErr = errors.New("incorrect type for arg")
    42  
    43  func main() {
    44  	defer panichandler.HandlePanic()
    45  	exitStatus := parse(os.Args[1:], &common.Commands)
    46  	if exitStatus == switchToV2 {
    47  		exitStatus = parse(os.Args[1:], &common.FallbackCommands)
    48  	}
    49  	if exitStatus != 0 {
    50  		os.Exit(exitStatus)
    51  	}
    52  }
    53  
    54  func parse(args []string, commandList interface{}) int {
    55  	parser := flags.NewParser(commandList, flags.HelpFlag)
    56  	parser.CommandHandler = executionWrapper
    57  	extraArgs, err := parser.ParseArgs(args)
    58  	if err == nil {
    59  		return 0
    60  	} else if _, ok := err.(translatableerror.V3V2SwitchError); ok {
    61  		return switchToV2
    62  	} else if flagErr, ok := err.(*flags.Error); ok {
    63  		return handleFlagErrorAndCommandHelp(flagErr, parser, extraArgs, args, commandList)
    64  	} else if err == ErrFailed {
    65  		return 1
    66  	} else if err == ParseErr {
    67  		fmt.Println()
    68  		parse([]string{"help", args[0]}, commandList)
    69  		return 1
    70  	} else if exitError, ok := err.(*ssh.ExitError); ok {
    71  		return exitError.ExitStatus()
    72  	}
    73  
    74  	fmt.Fprintf(os.Stderr, "Unexpected error: %s\n", err.Error())
    75  	return 1
    76  }
    77  
    78  func handleFlagErrorAndCommandHelp(flagErr *flags.Error, parser *flags.Parser, extraArgs []string, originalArgs []string, commandList interface{}) int {
    79  	switch flagErr.Type {
    80  	case flags.ErrHelp, flags.ErrUnknownFlag, flags.ErrExpectedArgument, flags.ErrInvalidChoice:
    81  		_, found := reflect.TypeOf(common.Commands).FieldByNameFunc(
    82  			func(fieldName string) bool {
    83  				field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName)
    84  				return parser.Active != nil && parser.Active.Name == field.Tag.Get("command")
    85  			},
    86  		)
    87  
    88  		if found && flagErr.Type == flags.ErrUnknownFlag && (parser.Active.Name == "set-env" || parser.Active.Name == "v3-set-env") {
    89  			newArgs := []string{}
    90  			for _, arg := range originalArgs {
    91  				if arg[0] == '-' {
    92  					newArgs = append(newArgs, fmt.Sprintf("%s%s", flag.WorkAroundPrefix, arg))
    93  				} else {
    94  					newArgs = append(newArgs, arg)
    95  				}
    96  			}
    97  			parse(newArgs, commandList)
    98  			return 0
    99  		}
   100  
   101  		if flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice {
   102  			fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error())
   103  		}
   104  
   105  		var helpErrored int
   106  		if found {
   107  			helpErrored = parse([]string{"help", parser.Active.Name}, commandList)
   108  		} else {
   109  			switch len(extraArgs) {
   110  			case 0:
   111  				helpErrored = parse([]string{"help"}, commandList)
   112  			case 1:
   113  				if !isOption(extraArgs[0]) || (len(originalArgs) > 1 && extraArgs[0] == "-a") {
   114  					helpErrored = parse([]string{"help", extraArgs[0]}, commandList)
   115  				} else {
   116  					helpErrored = parse([]string{"help"}, commandList)
   117  				}
   118  			default:
   119  				if isCommand(extraArgs[0]) {
   120  					helpErrored = parse([]string{"help", extraArgs[0]}, commandList)
   121  				} else {
   122  					helpErrored = parse(extraArgs[1:], commandList)
   123  				}
   124  			}
   125  		}
   126  
   127  		if helpErrored > 0 || flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice {
   128  			return 1
   129  		}
   130  	case flags.ErrRequired, flags.ErrMarshal:
   131  		fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error())
   132  		parse([]string{"help", originalArgs[0]}, commandList)
   133  		return 1
   134  	case flags.ErrUnknownCommand:
   135  		cmd.Main(os.Getenv("CF_TRACE"), os.Args)
   136  	case flags.ErrCommandRequired:
   137  		if common.Commands.VerboseOrVersion {
   138  			parse([]string{"version"}, commandList)
   139  		} else {
   140  			parse([]string{"help"}, commandList)
   141  		}
   142  	default:
   143  		fmt.Fprintf(os.Stderr, "Unexpected flag error\ntype: %s\nmessage: %s\n", flagErr.Type, flagErr.Error())
   144  	}
   145  	return 0
   146  }
   147  
   148  func isCommand(s string) bool {
   149  	_, found := reflect.TypeOf(common.Commands).FieldByNameFunc(
   150  		func(fieldName string) bool {
   151  			field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName)
   152  			return s == field.Tag.Get("command") || s == field.Tag.Get("alias")
   153  		})
   154  
   155  	return found
   156  }
   157  
   158  func isOption(s string) bool {
   159  	return strings.HasPrefix(s, "-")
   160  }
   161  
   162  func executionWrapper(cmd flags.Commander, args []string) error {
   163  	cfConfig, configErr := configv3.LoadConfig(configv3.FlagOverride{
   164  		Verbose: common.Commands.VerboseOrVersion,
   165  	})
   166  	if configErr != nil {
   167  		if _, ok := configErr.(translatableerror.EmptyConfigError); !ok {
   168  			return configErr
   169  		}
   170  	}
   171  
   172  	commandUI, err := ui.NewUI(cfConfig)
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	err = cfConfig.CreatePluginHome()
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	// TODO: when the line in the old code under `cf` which calls
   183  	// configv3.LoadConfig() is finally removed, then we should replace the code
   184  	// path above with the following:
   185  	//
   186  	// var configErrTemplate string
   187  	// if configErr != nil {
   188  	// 	if ce, ok := configErr.(translatableerror.EmptyConfigError); ok {
   189  	// 		configErrTemplate = ce.Error()
   190  	// 	} else {
   191  	// 		return configErr
   192  	// 	}
   193  	// }
   194  
   195  	// commandUI, err := ui.NewUI(cfConfig)
   196  	// if err != nil {
   197  	// 	return err
   198  	// }
   199  
   200  	// if configErr != nil {
   201  	//   commandUI.DisplayWarning(configErrTemplate, map[string]interface{}{
   202  	//   	"FilePath": configv3.ConfigFilePath(),
   203  	//   })
   204  	// }
   205  
   206  	defer func() {
   207  		configWriteErr := configv3.WriteConfig(cfConfig)
   208  		if configWriteErr != nil {
   209  			fmt.Fprintf(os.Stderr, "Error writing config: %s", configWriteErr.Error())
   210  		}
   211  	}()
   212  
   213  	if extendedCmd, ok := cmd.(command.ExtendedCommander); ok {
   214  		log.SetOutput(os.Stderr)
   215  		log.SetLevel(log.Level(cfConfig.LogLevel()))
   216  
   217  		err = extendedCmd.Setup(cfConfig, commandUI)
   218  		if err != nil {
   219  			return handleError(err, commandUI)
   220  		}
   221  		return handleError(extendedCmd.Execute(args), commandUI)
   222  	}
   223  
   224  	return fmt.Errorf("command does not conform to ExtendedCommander")
   225  }
   226  
   227  func handleError(passedErr error, commandUI UI) error {
   228  	if passedErr == nil {
   229  		return nil
   230  	}
   231  
   232  	translatedErr := translatableerror.ConvertToTranslatableError(passedErr)
   233  
   234  	switch typedErr := translatedErr.(type) {
   235  	case translatableerror.V3V2SwitchError:
   236  		log.Info("Received a V3V2SwitchError - switch to the V2 version of the command")
   237  		return passedErr
   238  	case TriggerLegacyMain:
   239  		if typedErr.Error() != "" {
   240  			commandUI.DisplayWarning("")
   241  			commandUI.DisplayWarning(typedErr.Error())
   242  		}
   243  
   244  		cmd.Main(os.Getenv("CF_TRACE"), os.Args)
   245  	case *ssh.ExitError:
   246  		exitStatus := typedErr.ExitStatus()
   247  		if sig := typedErr.Signal(); sig != "" {
   248  			commandUI.DisplayText("Process terminated by signal: {{.Signal}}. Exited with {{.ExitCode}}", map[string]interface{}{
   249  				"Signal":   sig,
   250  				"ExitCode": exitStatus,
   251  			})
   252  		}
   253  		return passedErr
   254  	}
   255  
   256  	commandUI.DisplayError(translatedErr)
   257  
   258  	if _, ok := translatedErr.(DisplayUsage); ok {
   259  		return ParseErr
   260  	}
   261  
   262  	return ErrFailed
   263  }