...

Source file src/sigs.k8s.io/cli-utils/pkg/errors/errors.go

Documentation: sigs.k8s.io/cli-utils/pkg/errors

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package errors
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"reflect"
    12  	"strings"
    13  	"text/template"
    14  
    15  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    16  	"sigs.k8s.io/cli-utils/pkg/inventory"
    17  	"sigs.k8s.io/cli-utils/pkg/manifestreader"
    18  )
    19  
    20  const (
    21  	DefaultErrorExitCode = 1
    22  )
    23  
    24  var errorMsgForType map[reflect.Type]string
    25  var statusCodeForType map[reflect.Type]int
    26  
    27  //nolint:gochecknoinits
    28  func init() {
    29  	errorMsgForType = make(map[reflect.Type]string)
    30  	errorMsgForType[reflect.TypeOf(inventory.NoInventoryObjError{})] = `
    31  Package uninitialized. Please run "{{.cmdNameBase}} init" command.
    32  
    33  The package needs to be initialized to generate the template
    34  which will store state for resource sets. This state is
    35  necessary to perform functionality such as deleting an entire
    36  package or automatically deleting omitted resources (pruning).
    37  `
    38  
    39  	errorMsgForType[reflect.TypeOf(inventory.MultipleInventoryObjError{})] = `
    40  Package has multiple inventory object templates.
    41  
    42  The package should have one and only one inventory object template.
    43  `
    44  
    45  	errorMsgForType[reflect.TypeOf(manifestreader.UnknownTypesError{})] = `
    46  Unknown type(s) encountered. Every type must either be already installed in the cluster or the CRD must be among the applied manifests.
    47  
    48  {{- range .err.GroupKinds}}
    49  {{ printf "%s" . }}
    50  {{- end}}
    51  `
    52  
    53  	statusCodeForType = make(map[reflect.Type]int)
    54  }
    55  
    56  // CheckErr looks up the appropriate error message and exit status for known
    57  // errors. It will print the information to the provided io.Writer. If we
    58  // don't know the error, it delegates to the error handling in cmdutil.
    59  func CheckErr(w io.Writer, err error, cmdNameBase string) {
    60  	errText, found := textForError(err, cmdNameBase)
    61  	if found {
    62  		exitStatus := findErrExitCode(err)
    63  		if len(errText) > 0 {
    64  			if !strings.HasSuffix(errText, "\n") {
    65  				errText += "\n"
    66  			}
    67  			fmt.Fprint(w, errText)
    68  		}
    69  		os.Exit(exitStatus)
    70  	}
    71  
    72  	cmdutil.CheckErr(err)
    73  }
    74  
    75  // textForError looks up the error message based on the type of the error.
    76  func textForError(baseErr error, cmdNameBase string) (string, bool) {
    77  	errType, found := findErrType(baseErr)
    78  	if !found {
    79  		return "", false
    80  	}
    81  	tmplText, found := errorMsgForType[errType]
    82  	if !found {
    83  		return "", false
    84  	}
    85  
    86  	tmpl, err := template.New("errMsg").Parse(tmplText)
    87  	if err != nil {
    88  		// Just return false here instead of the error. It will just
    89  		// mean a less informative error message and we rather show the
    90  		// original error.
    91  		return "", false
    92  	}
    93  	var b bytes.Buffer
    94  	err = tmpl.Execute(&b, map[string]interface{}{
    95  		"cmdNameBase": cmdNameBase,
    96  		"err":         baseErr,
    97  	})
    98  	if err != nil {
    99  		return "", false
   100  	}
   101  	return strings.TrimSpace(b.String()), true
   102  }
   103  
   104  // findErrType finds the type of the error. It returns the real type in the
   105  // event the error is actually a pointer to a type.
   106  func findErrType(err error) (reflect.Type, bool) {
   107  	switch reflect.ValueOf(err).Kind() {
   108  	case reflect.Ptr:
   109  		// If the value of the interface is a pointer, we use the type
   110  		// of the real value.
   111  		return reflect.ValueOf(err).Elem().Type(), true
   112  	case reflect.Struct:
   113  		return reflect.TypeOf(err), true
   114  	default:
   115  		return nil, false
   116  	}
   117  }
   118  
   119  // findErrExitCode looks up if there is a defined error code for the provided
   120  // error type.
   121  func findErrExitCode(err error) int {
   122  	errType, found := findErrType(err)
   123  	if !found {
   124  		return DefaultErrorExitCode
   125  	}
   126  	if exitStatus, found := statusCodeForType[errType]; found {
   127  		return exitStatus
   128  	}
   129  	return DefaultErrorExitCode
   130  }
   131  

View as plain text