1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "flag" 21 "fmt" 22 "os" 23 "strconv" 24 "strings" 25 26 "github.com/pkg/errors" 27 28 errorsutil "k8s.io/apimachinery/pkg/util/errors" 29 ) 30 31 const ( 32 // DefaultErrorExitCode defines exit the code for failed action generally 33 DefaultErrorExitCode = 1 34 // PreFlightExitCode defines exit the code for preflight checks 35 PreFlightExitCode = 2 36 // ValidationExitCode defines the exit code validation checks 37 ValidationExitCode = 3 38 ) 39 40 var ( 41 // ErrInvalidSubCommandMsg is an error message returned on invalid subcommands 42 ErrInvalidSubCommandMsg = "invalid subcommand" 43 // ErrExit is an error returned when kubeadm is about to exit 44 ErrExit = errors.New("exit") 45 ) 46 47 // fatal prints the message if set and then exits. 48 func fatal(msg string, code int) { 49 if len(msg) > 0 { 50 // add newline if needed 51 if !strings.HasSuffix(msg, "\n") { 52 msg += "\n" 53 } 54 55 fmt.Fprint(os.Stderr, msg) 56 } 57 os.Exit(code) 58 } 59 60 // CheckErr prints a user friendly error to STDERR and exits with a non-zero 61 // exit code. Unrecognized errors will be printed with an "error: " prefix. 62 // 63 // This method is generic to the command in use and may be used by non-Kubectl 64 // commands. 65 func CheckErr(err error) { 66 checkErr(err, fatal) 67 } 68 69 // preflightError allows us to know if the error is a preflight error or not 70 // defining the interface here avoids an import cycle of pulling in preflight into the util package 71 type preflightError interface { 72 Preflight() bool 73 } 74 75 // checkErr formats a given error as a string and calls the passed handleErr 76 // func with that string and an exit code. 77 func checkErr(err error, handleErr func(string, int)) { 78 if err == nil { 79 return 80 } 81 82 msg := fmt.Sprintf("%s\nTo see the stack trace of this error execute with --v=5 or higher", err.Error()) 83 // check if the verbosity level in klog is high enough and print a stack trace. 84 f := flag.CommandLine.Lookup("v") 85 if f != nil { 86 // assume that the "v" flag contains a parseable Int32 as per klog's "Level" type alias, 87 // thus no error from ParseInt is handled here. 88 if v, e := strconv.ParseInt(f.Value.String(), 10, 32); e == nil { 89 // https://git.k8s.io/community/contributors/devel/sig-instrumentation/logging.md 90 // klog.V(5) - Trace level verbosity 91 if v > 4 { 92 msg = fmt.Sprintf("%+v", err) 93 } 94 } 95 } 96 97 switch { 98 case err == ErrExit: 99 handleErr("", DefaultErrorExitCode) 100 case strings.Contains(err.Error(), ErrInvalidSubCommandMsg): 101 handleErr(err.Error(), DefaultErrorExitCode) 102 default: 103 switch err.(type) { 104 case preflightError: 105 handleErr(msg, PreFlightExitCode) 106 case errorsutil.Aggregate: 107 handleErr(msg, ValidationExitCode) 108 109 default: 110 handleErr(msg, DefaultErrorExitCode) 111 } 112 } 113 } 114 115 // FormatErrMsg returns a human-readable string describing the slice of errors passed to the function 116 func FormatErrMsg(errs []error) string { 117 var errMsg string 118 for _, err := range errs { 119 errMsg = fmt.Sprintf("%s\t- %s\n", errMsg, err.Error()) 120 } 121 return errMsg 122 } 123