From 57bd93587438d2dba73ca3faafb24607bbee8a4b Mon Sep 17 00:00:00 2001 From: garywilson Date: Fri, 21 Nov 2025 23:19:35 -0500 Subject: [PATCH] improve: enhance error messages with context and actionable guidance Improve error messages throughout kubectl-neat to provide: - Clear context about what failed - Actionable guidance for users - Better debugging information - Fix typo: 'vaild' -> 'valid' This makes the tool more user-friendly and helps users understand and resolve issues when processing Kubernetes resources. --- .DS_Store | Bin 0 -> 6148 bytes cmd/neat.go | 24 ++++++++++++++---------- pkg/defaults/defaults.go | 14 +++++++------- 3 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..47e0af928b549c8ad01a2f316a05e115c3dc685c GIT binary patch literal 6148 zcmeHKyJ`bL3>@XIFr;yra(}^puo&kH@&ieH!2vrsV5h$>zn#$|X3b%6f%gF1yX@jAQeaj{;2}&*=nF3wYROlM-a2_X?X`veO#e0J oS~`QZVxqNTZnPCYG1L|lXcK`qY literal 0 HcmV?d00001 diff --git a/cmd/neat.go b/cmd/neat.go index 4df48e6..36af2a0 100644 --- a/cmd/neat.go +++ b/cmd/neat.go @@ -31,10 +31,14 @@ func Neat(in string) (string, error) { draft := in if in == "" { - return draft, fmt.Errorf("error in neatPod, input json is empty") + return draft, fmt.Errorf("input JSON is empty: provide a valid Kubernetes resource JSON to process") } if !gjson.Valid(in) { - return draft, fmt.Errorf("error in neatPod, input is not a vaild json: %s", in[:20]) + preview := in + if len(in) > 50 { + preview = in[:50] + "..." + } + return draft, fmt.Errorf("input is not valid JSON (typo: 'vaild' -> 'valid' in original message). First 50 chars: %s. Ensure your input is valid JSON before processing", preview) } kind := gjson.Get(in, "kind").String() @@ -55,7 +59,7 @@ func Neat(in string) (string, error) { // general neating draft, err = neatMetadata(draft) if err != nil { - return draft, fmt.Errorf("error in neatMetadata : %v", err) + return draft, fmt.Errorf("failed to process metadata (removing annotations/labels): %v. This may indicate malformed metadata in the input", err) } return draft, nil } @@ -63,29 +67,29 @@ func Neat(in string) (string, error) { // defaults neating draft, err = defaults.NeatDefaults(draft) if err != nil { - return draft, fmt.Errorf("error in neatDefaults : %v", err) + return draft, fmt.Errorf("failed to remove default values from spec: %v. This may indicate the resource type is not recognized or the spec is malformed", err) } // controllers neating draft, err = neatScheduler(draft) if err != nil { - return draft, fmt.Errorf("error in neatScheduler : %v", err) + return draft, fmt.Errorf("failed to remove scheduler fields (spec.nodeName): %v", err) } if kind == "Pod" { draft, err = neatServiceAccount(draft) if err != nil { - return draft, fmt.Errorf("error in neatServiceAccount : %v", err) + return draft, fmt.Errorf("failed to process service account fields for Pod: %v. This may indicate malformed volume or container configuration", err) } } // general neating draft, err = neatMetadata(draft) if err != nil { - return draft, fmt.Errorf("error in neatMetadata : %v", err) + return draft, fmt.Errorf("failed to process metadata (removing annotations/labels): %v. This may indicate malformed metadata in the input", err) } draft, err = neatStatus(draft) if err != nil { - return draft, fmt.Errorf("error in neatStatus : %v", err) + return draft, fmt.Errorf("failed to remove status field: %v", err) } draft, err = neatEmpty(draft) if err != nil { @@ -99,13 +103,13 @@ func neatMetadata(in string) (string, error) { var err error in, err = sjson.Delete(in, `metadata.annotations.kubectl\.kubernetes\.io/last-applied-configuration`) if err != nil { - return in, fmt.Errorf("error deleting last-applied-configuration : %v", err) + return in, fmt.Errorf("failed to delete kubectl.kubernetes.io/last-applied-configuration annotation: %v. The annotation may not exist (this is normal)", err) } // TODO: prettify this. gjson's @pretty is ok but setRaw the pretty code gives unwanted result newMeta := gjson.Get(in, "{metadata.name,metadata.namespace,metadata.labels,metadata.annotations}") in, err = sjson.Set(in, "metadata", newMeta.Value()) if err != nil { - return in, fmt.Errorf("error setting new metadata : %v", err) + return in, fmt.Errorf("failed to set cleaned metadata: %v. This may indicate the metadata structure is invalid", err) } return in, nil } diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go index 9beaecd..79163ec 100644 --- a/pkg/defaults/defaults.go +++ b/pkg/defaults/defaults.go @@ -22,7 +22,7 @@ func NeatDefaults(in string) (string, error) { var pom metav1.PartialObjectMetadata err = json.Unmarshal([]byte(in), &pom) if err != nil { - return "", fmt.Errorf("error unmarshaling as PartialObject : %v", err) + return "", fmt.Errorf("failed to parse Kubernetes resource metadata: %v. Ensure the input is valid Kubernetes YAML/JSON", err) } if !myscheme.Recognizes(pom.GroupVersionKind()) { return in, nil @@ -34,7 +34,7 @@ func NeatDefaults(in string) (string, error) { } pathsToDelete, err := flatMapJSON(specJSON.String(), "spec.") if err != nil { - return "", fmt.Errorf("error flattening json : %v", err) + return "", fmt.Errorf("failed to flatten spec JSON for default value detection: %v. The spec may contain invalid JSON structures", err) } for k, v := range pathsToDelete { isDefault, err := isDefault(k, v, in) @@ -62,7 +62,7 @@ func flatMapJSON(j string, prefix string) (map[string]interface{}, error) { var jParsed map[string]interface{} err := json.Unmarshal([]byte(j), &jParsed) if err != nil { - return nil, fmt.Errorf("error unmarshaling: %v", err) + return nil, fmt.Errorf("failed to parse JSON for flattening: %v. Ensure the JSON is valid", err) } res, err := flatten.Flatten(jParsed, prefix, flatten.DotStyle) if err != nil { @@ -84,7 +84,7 @@ func init() { func isDefault(path string, value interface{}, objJSON string) (bool, error) { computed, err := computeDefault(path, objJSON) if err != nil { - return false, fmt.Errorf("error computing default for '%s' : %v", path, err) + return false, fmt.Errorf("failed to compute default value for field '%s': %v. This may indicate the resource type is not fully supported", path, err) } expect := fmt.Sprintf("%v", value) return computed == expect, nil @@ -94,11 +94,11 @@ func isDefault(path string, value interface{}, objJSON string) (bool, error) { func computeDefault(path string, objJSON string) (string, error) { candidateJSON, err := sjson.Delete(objJSON, path) if err != nil { - return "", fmt.Errorf("error deleting path to default '%s' : %v", path, err) + return "", fmt.Errorf("failed to remove field '%s' for default computation: %v", path, err) } candidate, _, err := decoder.Decode([]byte(candidateJSON), nil, nil) if err != nil { - return "", fmt.Errorf("error decoding into kubernetes object : %v", err) + return "", fmt.Errorf("failed to decode Kubernetes object (API version may be unsupported): %v", err) } // why this doesn't work? @@ -107,7 +107,7 @@ func computeDefault(path string, objJSON string) (string, error) { resJSON, err := json.Marshal(candidate) if err != nil { - return "", fmt.Errorf("error marshaling kubernetes object : %v", err) + return "", fmt.Errorf("failed to marshal Kubernetes object after defaulting: %v", err) } defaultValue := gjson.Get(string(resJSON), path).String() return defaultValue, nil