summaryrefslogtreecommitdiff
path: root/pkg/errorhandling/errorhandling.go
blob: 9b456c9c08b97773aed4a6ae71d6ab770a9bebcd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package errorhandling

import (
	"errors"
	"os"
	"strings"

	"github.com/hashicorp/go-multierror"
	"github.com/sirupsen/logrus"
)

// JoinErrors converts the error slice into a single human-readable error.
func JoinErrors(errs []error) error {
	if len(errs) == 0 {
		return nil
	}

	// If there's just one error, return it.  This prevents the "%d errors
	// occurred:" header plus list from the multierror package.
	if len(errs) == 1 {
		return errs[0]
	}

	// `multierror` appends new lines which we need to remove to prevent
	// blank lines when printing the error.
	var multiE *multierror.Error
	multiE = multierror.Append(multiE, errs...)

	finalErr := multiE.ErrorOrNil()
	if finalErr == nil {
		return nil
	}
	return errors.New(strings.TrimSpace(finalErr.Error()))
}

// ErrorsToString converts the slice of errors into a slice of corresponding
// error messages.
func ErrorsToStrings(errs []error) []string {
	if len(errs) == 0 {
		return nil
	}
	strErrs := make([]string, len(errs))
	for i := range errs {
		strErrs[i] = errs[i].Error()
	}
	return strErrs
}

// StringsToErrors converts a slice of error messages into a slice of
// corresponding errors.
func StringsToErrors(strErrs []string) []error {
	if len(strErrs) == 0 {
		return nil
	}
	errs := make([]error, len(strErrs))
	for i := range strErrs {
		errs[i] = errors.New(strErrs[i])
	}
	return errs
}

// SyncQuiet syncs a file and logs any error. Should only be used within
// a defer.
func SyncQuiet(f *os.File) {
	if err := f.Sync(); err != nil {
		logrus.Errorf("Unable to sync file %s: %q", f.Name(), err)
	}
}

// CloseQuiet closes a file and logs any error. Should only be used within
// a defer.
func CloseQuiet(f *os.File) {
	if err := f.Close(); err != nil {
		logrus.Errorf("Unable to close file %s: %q", f.Name(), err)
	}
}

// Contains checks if err's message contains sub's message. Contains should be
// used iff either err or sub has lost type information (e.g., due to
// marshaling).  For typed errors, please use `errors.Contains(...)` or `Is()`
// in recent version of Go.
func Contains(err error, sub error) bool {
	return strings.Contains(err.Error(), sub.Error())
}

// PodConflictErrorModel is used in remote connections with podman
type PodConflictErrorModel struct {
	Errs []string
	Id   string //nolint:revive,stylecheck
}

// ErrorModel is used in remote connections with podman
type ErrorModel struct {
	// API root cause formatted for automated parsing
	// example: API root cause
	Because string `json:"cause"`
	// human error message, formatted for a human to read
	// example: human error message
	Message string `json:"message"`
	// HTTP response code
	// min: 400
	ResponseCode int `json:"response"`
}

func (e ErrorModel) Error() string {
	return e.Message
}

func (e ErrorModel) Cause() error {
	return errors.New(e.Because)
}

func (e ErrorModel) Code() int {
	return e.ResponseCode
}

func (e PodConflictErrorModel) Error() string {
	return strings.Join(e.Errs, ",")
}

func (e PodConflictErrorModel) Code() int {
	return 409
}

// Cause returns the most underlying error for the provided one. There is a
// maximum error depth of 100 to avoid endless loops. An additional error log
// message will be created if this maximum has reached.
func Cause(err error) (cause error) {
	cause = err

	const maxDepth = 100
	for i := 0; i <= maxDepth; i++ {
		res := errors.Unwrap(cause)
		if res == nil {
			return cause
		}
		cause = res
	}

	logrus.Errorf("Max error depth of %d reached, cannot unwrap until root cause: %v", maxDepth, err)
	return cause
}