Skip to content

Commit eb48b18

Browse files
committed
fix: convert unnamed errors to types
1 parent edcc8d1 commit eb48b18

File tree

4 files changed

+109
-18
lines changed

4 files changed

+109
-18
lines changed

admissionrequesthook.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package kubernetes
55

66
import (
7-
"fmt"
87
"net/http"
98

109
"github.com/gin-gonic/gin"
@@ -38,11 +37,11 @@ func (h AdmissionRequestHook) Call(req *admission.AdmissionRequest) (ValidationR
3837
case admission.Delete:
3938
callback = h.Delete
4039
default:
41-
return ValidationOk, fmt.Errorf("unknown admission operation: %s", req.Operation)
40+
return ValidationOk, ErrUnknownOperation(string(req.Operation))
4241
}
4342

4443
if callback == nil {
45-
return ValidationOk, fmt.Errorf("operation %s has no callback set", req.Operation)
44+
return ValidationOk, ErrNoCallback(string(req.Operation))
4645
}
4746

4847
// TODO: create parse request here

errors.go

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,100 @@ func (e ErrIncorrectType) Error() string {
9090
//
9191
// Array modification operations must use "-" for appending; explicit indices are not
9292
// supported during path extension.
93-
type ErrIndexNotation string
93+
type ErrIndexNotation struct{}
9494

9595
func (e ErrIndexNotation) Error() string {
9696
return "Cannot append to array using index notation"
9797
}
98+
99+
// ErrNoData is returned when a RawExtension object does not contain any data.
100+
// This occurs when both the Raw and Object fields are nil during conversion from
101+
// a runtime.RawExtension to a NamedObject.
102+
type ErrNoData struct{}
103+
104+
func (e ErrNoData) Error() string {
105+
return "No data found in raw object"
106+
}
107+
108+
// ErrMissingName is returned when a Kubernetes object does not have a name or
109+
// generateName field set in its metadata. This occurs during object validation
110+
// in NamedObjectFromUnstructured when neither metadata.name nor metadata.generateName
111+
// is present. All Kubernetes objects must have at least one of these fields defined.
112+
type ErrMissingName struct{}
113+
114+
func (e ErrMissingName) Error() string {
115+
return "Object does not have a name set"
116+
}
117+
118+
// ErrUnsupportedHashType is returned when attempting to hash a field with a type
119+
// that is not supported by the hashing algorithm. This occurs when:
120+
// - A field type cannot be converted to a hashable representation
121+
// - An unknown or complex type is encountered during object hashing
122+
//
123+
// The error string contains the field name and its type information.
124+
type ErrUnsupportedHashType string
125+
126+
func (e ErrUnsupportedHashType) Error() string {
127+
return string(e)
128+
}
129+
130+
// ErrUnknownOperation is returned when an admission webhook receives a request
131+
// with an operation type that is not recognized. Valid operations are Create,
132+
// Update, and Delete. This error occurs in AdmissionRequestHook.Call when the
133+
// admission request contains an unsupported operation.
134+
//
135+
// The error string contains the unknown operation name.
136+
type ErrUnknownOperation string
137+
138+
func (e ErrUnknownOperation) Error() string {
139+
return fmt.Sprintf("Unknown admission operation: %s", string(e))
140+
}
141+
142+
// ErrNoCallback is returned when an admission webhook receives a request for an
143+
// operation that does not have a validation callback registered. This occurs in
144+
// AdmissionRequestHook.Call when the operation (Create, Update, or Delete) handler
145+
// is nil. The request is still marked as validated to avoid blocking operations.
146+
//
147+
// The error string contains the operation name that lacks a callback.
148+
type ErrNoCallback string
149+
150+
func (e ErrNoCallback) Error() string {
151+
return fmt.Sprintf("Operation %s has no callback set", string(e))
152+
}
153+
154+
// ErrInvalidBoundObjectRef is returned when attempting to create a service account
155+
// token with an invalid bound object reference. This occurs when:
156+
// - A bound object reference is provided but is not a Pod
157+
// - The Kind field is set to something other than "pod" (case-insensitive)
158+
//
159+
// Service account tokens can only be bound to Pod objects or have no binding.
160+
type ErrInvalidBoundObjectRef struct{}
161+
162+
func (e ErrInvalidBoundObjectRef) Error() string {
163+
return "Bound object reference must be a pod or nil"
164+
}
165+
166+
// ErrNoToken is returned when a service account token request succeeds but the
167+
// response does not contain a token. This occurs in GetServiceAccountToken when
168+
// the Kubernetes API returns a successful response with an empty token field,
169+
// indicating an unexpected API behavior.
170+
type ErrNoToken struct{}
171+
172+
func (e ErrNoToken) Error() string {
173+
return "No token in server response"
174+
}
175+
176+
// ErrParseError is returned when parsing label selector components fails due to
177+
// type mismatches. This occurs in ParseLabelSelector when:
178+
// - A selector value is not a string
179+
// - matchLabels is not a map[string]string or map[string]interface{}
180+
// - matchExpressions is not the expected slice type
181+
// - A matchExpressions element is not a map[string]interface{}
182+
// - Required fields (key, operator, values) are not of the expected type
183+
//
184+
// The error string contains details about what failed to parse and the actual value.
185+
type ErrParseError string
186+
187+
func (e ErrParseError) Error() string {
188+
return string(e)
189+
}

labelselector.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func ParseLabelSelector(obj map[string]interface{}) (metav1.LabelSelector, error
3939
for k, v := range obj {
4040
stringValue, ok := v.(string)
4141
if !ok {
42-
return selector, fmt.Errorf("failed to parse selector[%s] as string : %v", k, v)
42+
return selector, ErrParseError(fmt.Sprintf("failed to parse selector[%s] as string : %v", k, v))
4343
}
4444
selector.MatchLabels[k] = stringValue
4545
}
@@ -52,14 +52,14 @@ func ParseLabelSelector(obj map[string]interface{}) (metav1.LabelSelector, error
5252
// Support any-type key/value maps (named objects)
5353
untyped, ok := matchLabels.(map[string]interface{})
5454
if !ok {
55-
return selector, fmt.Errorf("failed to parse matchLabels as map[string]string or map[string]interface{} : %v", matchLabels)
55+
return selector, ErrParseError(fmt.Sprintf("failed to parse matchLabels as map[string]string or map[string]interface{} : %v", matchLabels))
5656
}
5757

5858
selector.MatchLabels = make(map[string]string)
5959
for k, v := range untyped {
6060
stringValue, ok := v.(string)
6161
if !ok {
62-
return selector, fmt.Errorf("failed to parse matchLabels[%s] as string : %v", k, v)
62+
return selector, ErrParseError(fmt.Sprintf("failed to parse matchLabels[%s] as string : %v", k, v))
6363
}
6464
selector.MatchLabels[k] = stringValue
6565
}
@@ -72,14 +72,14 @@ func ParseLabelSelector(obj map[string]interface{}) (metav1.LabelSelector, error
7272
// Support any-type key/value maps (named objects)
7373
untypedList, ok := matchExpressions.([]interface{})
7474
if !ok {
75-
return selector, fmt.Errorf("failed to parse matchExpressions as []metav1.LabelSelectorRequirement or []interface{} : %v", matchExpressions)
75+
return selector, ErrParseError(fmt.Sprintf("failed to parse matchExpressions as []metav1.LabelSelectorRequirement or []interface{} : %v", matchExpressions))
7676
}
7777

7878
selector.MatchExpressions = make([]metav1.LabelSelectorRequirement, 0, len(untypedList))
7979
for i, v := range untypedList {
8080
untypedMap, ok := v.(map[string]interface{})
8181
if !ok {
82-
return selector, fmt.Errorf("failed to parse matchExpressions[%d] as map[string]interface{} : %v", i, v)
82+
return selector, ErrParseError(fmt.Sprintf("failed to parse matchExpressions[%d] as map[string]interface{} : %v", i, v))
8383
}
8484

8585
parsed, err := parseLabelSelectorRequirement(untypedMap)
@@ -104,27 +104,27 @@ func parseLabelSelectorRequirement(obj map[string]interface{}) (metav1.LabelSele
104104

105105
req.Key, ok = obj["key"].(string)
106106
if !ok {
107-
return req, fmt.Errorf("failed to parse key as string : %v", obj["key"])
107+
return req, ErrParseError(fmt.Sprintf("failed to parse key as string : %v", obj["key"]))
108108
}
109109

110110
operatorStr, ok := obj["operator"].(string)
111111
if !ok {
112-
return req, fmt.Errorf("failed to parse operator as metav1.LabelSelectorOperator (string) : %v", obj["operator"])
112+
return req, ErrParseError(fmt.Sprintf("failed to parse operator as metav1.LabelSelectorOperator (string) : %v", obj["operator"]))
113113
}
114114
req.Operator = metav1.LabelSelectorOperator(operatorStr)
115115

116116
req.Values, ok = obj["values"].([]string)
117117
if !ok {
118118
untypedList, ok := obj["values"].([]interface{})
119119
if !ok {
120-
return req, fmt.Errorf("failed to parse values as []string or []interface{} : %v", obj["values"])
120+
return req, ErrParseError(fmt.Sprintf("failed to parse values as []string or []interface{} : %v", obj["values"]))
121121
}
122122

123123
req.Values = make([]string, 0, len(untypedList))
124124
for i, v := range untypedList {
125125
stringValue, ok := v.(string)
126126
if !ok {
127-
return req, fmt.Errorf("failed to parse values[%d] as string : %v", i, v)
127+
return req, ErrParseError(fmt.Sprintf("failed to parse values[%d] as string : %v", i, v))
128128
}
129129

130130
req.Values = append(req.Values, stringValue)

namedobject.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewNamedObject(name string) NamedObject {
3838
func NamedObjectFromRaw(data *runtime.RawExtension) (NamedObject, error) {
3939
if data.Raw == nil {
4040
if data.Object == nil {
41-
return NamedObject{}, fmt.Errorf("no data found in raw object")
41+
return NamedObject{}, ErrNoData{}
4242
}
4343
var err error
4444
if data.Raw, err = jsoniter.Marshal(data.Object); err != nil {
@@ -67,7 +67,7 @@ func NamedObjectFromUnstructured(unstructuredObj unstructured.Unstructured) (Nam
6767
// "generateName" is used by pods before a, e.g., ReplicaSet controler
6868
// processed the pod.
6969
if !obj.Has(PathMetadataName) && !obj.Has(PathMetadataGenerateName) {
70-
return obj, fmt.Errorf("object does not have a name set")
70+
return obj, ErrMissingName{}
7171
}
7272

7373
return obj, nil
@@ -537,7 +537,7 @@ func doHash(hasher hash.Hash64, k string, iv interface{}) error {
537537
return err
538538

539539
default:
540-
return fmt.Errorf("cannot create hash for field %s of type %T", k, v)
540+
return ErrUnsupportedHashType(fmt.Sprintf("cannot create hash for field %s of type %T", k, v))
541541
}
542542
}
543543

@@ -608,7 +608,7 @@ func (obj NamedObject) GeneratePatch(path Path, value interface{}) (Path, interf
608608
}
609609

610610
case ArrayNotationIndex:
611-
return validPath, value, ErrIndexNotation("")
611+
return validPath, value, ErrIndexNotation{}
612612
}
613613

614614
extendedValue := parentNode
@@ -660,7 +660,7 @@ func (obj NamedObject) GeneratePatch(path Path, value interface{}) (Path, interf
660660
}
661661

662662
case ArrayNotationIndex:
663-
return validPath, value, ErrIndexNotation("")
663+
return validPath, value, ErrIndexNotation{}
664664
}
665665
}
666666

0 commit comments

Comments
 (0)