errors

package
v0.2.0-beta.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

README

Errors - Structured Error Handling

Go Reference

The errors package provides structured error types with contextual information for Kubernetes resource operations. All Kure packages use this instead of fmt.Errorf.

Overview

Errors in Kure carry context: the type of error, what resource was affected, suggestions for fixing the problem, and the original cause. This makes debugging easier and enables programmatic error handling.

Error Types

Type Use Case Key Fields
ValidationError Field validation failures Field, Value, ValidValues, Suggestion
ResourceError Resource-specific issues Kind, Name, Namespace, Available
PatchError Patch operation failures Operation, Path, ResourceName
ParseError File/YAML parsing errors Source, Line, Column
FileError File system operations Operation, Path
ConfigError Configuration problems Source, Field, Value, ValidValues

Usage

Each Go block in this section is the body of an Example function in example_test.go, which go test runs: it imports this package as errors, and fmt for the lines that print the errors the example built. In your own code, return these errors rather than print them. Wrap and Wrapf return nil for a nil error, so they need no if err != nil guard.

Wrapping Errors
err := errors.New("connection refused")

// Wrap with context
wrapped := errors.Wrap(err, "failed to load cluster config")

// Wrap with formatted message
formatted := errors.Wrapf(err, "failed to fetch %s/%s", "Deployment", "my-app")

fmt.Println(wrapped)
fmt.Println(formatted)
Creating Errors
// Simple error
simple := errors.New("invalid configuration")

// Formatted error
formatted := errors.Errorf("unknown generator: %s", "AppWorkload")

fmt.Println(simple)
fmt.Println(formatted)
Typed Errors
originalErr := errors.New("original cause")

// Validation error with suggestion
validation := errors.NewValidationError(
    "replicas",         // field
    "-1",               // value
    "Deployment",       // component
    []string{"1", "3"}, // valid values
)

// Resource not found
notFound := errors.ResourceNotFoundError(
    "Deployment",                   // resource type
    "my-app",                       // name
    "default",                      // namespace
    []string{"web-app", "api-app"}, // available resources
)

// Patch error
patch := errors.NewPatchError(
    "set",             // operation
    "spec.replicas",   // path
    "my-deployment",   // resource name
    "field not found", // reason
    originalErr,       // cause
)

// Parse error with location
parse := errors.NewParseError(
    "config.yaml",  // source file
    "invalid YAML", // reason
    42,             // line
    10,             // column
    originalErr,    // cause
)

// File error
file := errors.NewFileError("read", "/path/to/file", "permission denied", originalErr)

// Configuration error
config := errors.NewConfigError(
    "mise.toml",              // source
    "go",                     // field
    "1.21",                   // value
    "version too old",        // reason
    []string{"1.23", "1.24"}, // valid values
)

for _, err := range []error{validation, notFound, patch, parse, file, config} {
    fmt.Println(err)
}
Inspecting Errors

IsKureError, GetKureError and IsType look through wrapping, so they find a typed error anywhere in the chain:

err := errors.Wrap(
    errors.NewValidationError("replicas", "-1", "Deployment", []string{"1", "3"}),
    "load cluster config",
)

// Check if error is a Kure error
if errors.IsKureError(err) {
    kErr := errors.GetKureError(err)
    fmt.Println(kErr.Type())
    fmt.Println(kErr.Suggestion())
}

// Check specific error type
if errors.IsType(err, errors.ErrorTypeValidation) {
    // Handle validation error
    fmt.Println("validation error:", err)
}

Predefined Errors

A small set of sentinels is predefined, one for each error Kure code actually returns, so callers can match them with the standard library's errors.Is:

errors.ErrNilPodSpec       // PSA validators (pkg/kubernetes)
errors.ErrNilContainer     // PSA validators (pkg/kubernetes)
errors.ErrNilBundle        // bundle validation (pkg/stack)
errors.ErrNilObject
errors.ErrNilRuntimeObject
errors.ErrGVKNotFound
errors.ErrGVKNotAllowed
errors.ErrUnsupportedKind

This package does not re-export Is or As, so import both packages under distinct names:

import (
    stderrors "errors"

    kerrors "github.com/go-kure/kure/pkg/errors"
)

The block below is the body of an Example function in example_sentinel_test.go, which go test runs: it imports the two packages as above, github.com/go-kure/kure/pkg/kubernetes for a function that returns a sentinel, and fmt.

// A PSA validator given no pod spec returns the ErrNilPodSpec sentinel.
err := kubernetes.ValidatePodSpecPSA(nil, kubernetes.PSARestricted)

if stderrors.Is(err, kerrors.ErrNilPodSpec) {
    // handle nil pod spec
    fmt.Println("nil pod spec:", err)
}

A sentinel that no Kure code returns is removed rather than kept exported: callers could compare against it, but nothing would ever produce it. TestExportedSentinelsHaveProducers fails the build if an exported Err* sentinel, declared in any file of this package, has no producer outside it: a non-test reference through an import of this package. A comparison (errors.Is/errors.As argument, ==/!= operand, switch tag or case) is not a producer, and neither is an assignment to the blank identifier (var _ = errors.ErrX). The check reads the source, so any other mention, such as a sentinel stored in a variable and only then compared, or passed to a logger, still counts as produced.

All Kure packages import this package for error handling. Never use fmt.Errorf directly.

Documentation

Overview

Package errors provides structured error types and handling utilities for the Kure library and kurel tool.

Overview

This package extends Go's standard error handling with domain-specific error types that provide structured information for Kubernetes resource validation, file operations, and configuration errors.

Error Types

The package provides several specialized error constructors:

Predefined Errors

A small set of sentinels is predefined, one for each error kure code actually returns; a sentinel no kure code returns is removed, not kept:

// Nil resource checks
errors.ErrNilPodSpec
errors.ErrNilContainer
errors.ErrNilBundle
errors.ErrNilObject
errors.ErrNilRuntimeObject

// GVK and kind errors
errors.ErrGVKNotFound
errors.ErrGVKNotAllowed
errors.ErrUnsupportedKind

Error Wrapping

The package provides wrappers compatible with Go's error unwrapping:

// Wrap with message
err := errors.Wrap(originalErr, "failed to load config")

// Wrap with formatted message
err := errors.Wrapf(originalErr, "failed to process %s", filename)

// Check wrapped errors with the standard library's errors.Is; this
// package does not re-export it, so import the two under distinct names
// (stderrors "errors", kerrors "github.com/go-kure/kure/pkg/errors")
if stderrors.Is(err, kerrors.ErrNilPodSpec) {
    // handle nil pod spec
}

Resource Validation Errors

Resource validation errors include structured fields:

err := errors.ResourceValidationError(
    "Deployment",           // resourceType
    "my-app",              // name
    "spec.replicas",       // field
    "must be positive",    // reason
    originalErr,           // cause (optional)
)

These errors can be introspected for automated handling:

var resErr *kerrors.ResourceError
if stderrors.As(err, &resErr) {
    fmt.Printf("Resource: %s/%s\n", resErr.ResourceType, resErr.Name)
}

File Errors

File operation errors include the operation type and path:

err := errors.NewFileError("read", "/path/to/file", "permission denied", nil)

Integration

All error types implement the standard error interface and support Go 1.13+ error wrapping with errors.Is and errors.As.

Example (Sentinel)
package main

import (
	stderrors "errors"
	"fmt"

	kerrors "github.com/go-kure/kure/pkg/errors"
	"github.com/go-kure/kure/pkg/kubernetes"
)

func main() {
	// A PSA validator given no pod spec returns the ErrNilPodSpec sentinel.
	err := kubernetes.ValidatePodSpecPSA(nil, kubernetes.PSARestricted)

	if stderrors.Is(err, kerrors.ErrNilPodSpec) {
		// handle nil pod spec
		fmt.Println("nil pod spec:", err)
	}
}
Output:
nil pod spec: validation failed for PodSpec '' field 'spec': pod spec cannot be nil

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrGVKNotFound   = errors.New("could not determine GroupVersionKind")
	ErrGVKNotAllowed = errors.New("GroupVersionKind is not allowed")
	ErrNilObject     = errors.New("provided object is nil")
)

Standard error variables using the standard library

View Source
var (
	ErrNilPodSpec   = ResourceValidationError("PodSpec", "", "spec", "pod spec cannot be nil", nil)
	ErrNilContainer = ResourceValidationError("Container", "", "container", "container cannot be nil", nil)
	ErrNilBundle    = ResourceValidationError("Bundle", "", "bundle", "bundle cannot be nil", nil)
)

Resource validation errors still returned by kure: the PSA validators in pkg/kubernetes and the stack's bundle checks. Sentinels no kure code returns are removed rather than kept exported; TestExportedSentinelsHaveProducers enforces that (go-kure/kure#758).

View Source
var (
	ErrNilRuntimeObject = errors.New("nil runtime object provided")
	ErrUnsupportedKind  = errors.New("unsupported object kind")
)

Common parse/processing errors

Functions

func Errorf

func Errorf(format string, args ...any) error

Errorf creates a new formatted error using Go's standard error formatting.

Example
package main

import (
	"fmt"

	"github.com/go-kure/kure/pkg/errors"
)

func main() {
	// Simple error
	simple := errors.New("invalid configuration")

	// Formatted error
	formatted := errors.Errorf("unknown generator: %s", "AppWorkload")

	fmt.Println(simple)
	fmt.Println(formatted)
}
Output:
invalid configuration
unknown generator: AppWorkload

func IsKureError

func IsKureError(err error) bool

IsKureError checks if an error is a Kure-specific error

Example
package main

import (
	"fmt"

	"github.com/go-kure/kure/pkg/errors"
)

func main() {
	err := errors.Wrap(
		errors.NewValidationError("replicas", "-1", "Deployment", []string{"1", "3"}),
		"load cluster config",
	)

	// Check if error is a Kure error
	if errors.IsKureError(err) {
		kErr := errors.GetKureError(err)
		fmt.Println(kErr.Type())
		fmt.Println(kErr.Suggestion())
	}

	// Check specific error type
	if errors.IsType(err, errors.ErrorTypeValidation) {
		// Handle validation error
		fmt.Println("validation error:", err)
	}
}
Output:
validation
Valid values are: 1, 3
validation error: load cluster config: invalid replicas for Deployment: -1

func IsType

func IsType(err error, errType ErrorType) bool

IsType checks if an error is of a specific Kure error type

func New

func New(message string) error

New creates a new error with the given message.

func Wrap

func Wrap(err error, message string) error

Wrap wraps an error with a message using Go's standard error wrapping.

Example
package main

import (
	"fmt"

	"github.com/go-kure/kure/pkg/errors"
)

func main() {
	err := errors.New("connection refused")

	// Wrap with context
	wrapped := errors.Wrap(err, "failed to load cluster config")

	// Wrap with formatted message
	formatted := errors.Wrapf(err, "failed to fetch %s/%s", "Deployment", "my-app")

	fmt.Println(wrapped)
	fmt.Println(formatted)
}
Output:
failed to load cluster config: connection refused
failed to fetch Deployment/my-app: connection refused

func Wrapf

func Wrapf(err error, format string, args ...any) error

Wrapf wraps an error with a formatted message using Go's standard error wrapping.

Types

type BaseError

type BaseError struct {
	ErrType    ErrorType      `json:"type"`
	Message    string         `json:"message"`
	Cause      error          `json:"cause,omitempty"`
	ErrContext map[string]any `json:"context,omitempty"`
	Help       string         `json:"suggestion,omitempty"`
}

BaseError provides common functionality for all Kure errors

func (*BaseError) Context

func (e *BaseError) Context() map[string]any

func (*BaseError) Error

func (e *BaseError) Error() string

func (*BaseError) Suggestion

func (e *BaseError) Suggestion() string

func (*BaseError) Type

func (e *BaseError) Type() ErrorType

func (*BaseError) Unwrap

func (e *BaseError) Unwrap() error

type ConfigError

type ConfigError struct {
	*BaseError
	Source      string   `json:"source"`
	Field       string   `json:"field"`
	ValidValues []string `json:"validValues,omitempty"`
}

ConfigError represents configuration errors

func NewConfigError

func NewConfigError(source, field, value, reason string, validValues []string) *ConfigError

type ErrorType

type ErrorType string

ErrorType represents the category of error

const (
	ErrorTypeValidation    ErrorType = "validation"
	ErrorTypeResource      ErrorType = "resource"
	ErrorTypePatch         ErrorType = "patch"
	ErrorTypeParse         ErrorType = "parse"
	ErrorTypeFile          ErrorType = "file"
	ErrorTypeConfiguration ErrorType = "configuration"
	ErrorTypeInternal      ErrorType = "internal"
	ErrorTypePSA           ErrorType = "psa"
)

type FileError

type FileError struct {
	*BaseError
	Operation string `json:"operation"`
	Path      string `json:"path"`
}

FileError represents file operation errors

func NewFileError

func NewFileError(operation, path, reason string, cause error) *FileError

type KureError

type KureError interface {
	error
	Type() ErrorType
	Suggestion() string
	Context() map[string]any
}

KureError is the base interface for all Kure-specific errors

func GetKureError

func GetKureError(err error) KureError

GetKureError extracts a KureError from an error chain

type PSAViolationError

type PSAViolationError struct {
	*BaseError
	Field string `json:"field"` // path relative to PodSpec
	Level string `json:"level"` // "baseline" or "restricted"
}

PSAViolationError represents a Pod Security Standards violation with field path information.

func NewPSAViolationError

func NewPSAViolationError(field, level, message string) *PSAViolationError

type ParseError

type ParseError struct {
	*BaseError
	Source string `json:"source"`
	Line   int    `json:"line,omitempty"`
	Column int    `json:"column,omitempty"`
}

ParseError represents parsing errors with location information

func NewParseError

func NewParseError(source, reason string, line, column int, cause error) *ParseError

type ParseErrors

type ParseErrors struct {
	Errors []error
}

ParseErrors aggregates multiple errors returned during YAML decoding. It implements the error interface and unwraps to the underlying errors.

func (*ParseErrors) Error

func (pe *ParseErrors) Error() string

func (*ParseErrors) Unwrap

func (pe *ParseErrors) Unwrap() []error

type PatchError

type PatchError struct {
	*BaseError
	Operation    string `json:"operation"`
	Path         string `json:"path"`
	ResourceName string `json:"resourceName"`
}

PatchError represents patch-specific errors

func NewPatchError

func NewPatchError(operation, path, resourceName, reason string, cause error) *PatchError

type ResourceError

type ResourceError struct {
	*BaseError
	ResourceType string   `json:"resourceType"`
	Name         string   `json:"name"`
	Namespace    string   `json:"namespace,omitempty"`
	Available    []string `json:"available,omitempty"`
}

ResourceError represents resource-related errors

func ResourceNotFoundError

func ResourceNotFoundError(resourceType, name, namespace string, available []string) *ResourceError

func ResourceValidationError

func ResourceValidationError(resourceType, name, field, reason string, cause error) *ResourceError

type ValidationError

type ValidationError struct {
	*BaseError
	Field       string   `json:"field"`
	Value       string   `json:"value"`
	ValidValues []string `json:"validValues,omitempty"`
	Component   string   `json:"component"`
}

ValidationError represents validation failures with suggestions

func NewValidationError

func NewValidationError(field, value, component string, validValues []string) *ValidationError
Example
package main

import (
	"fmt"

	"github.com/go-kure/kure/pkg/errors"
)

func main() {
	originalErr := errors.New("original cause")

	// Validation error with suggestion
	validation := errors.NewValidationError(
		"replicas",         // field
		"-1",               // value
		"Deployment",       // component
		[]string{"1", "3"}, // valid values
	)

	// Resource not found
	notFound := errors.ResourceNotFoundError(
		"Deployment",                   // resource type
		"my-app",                       // name
		"default",                      // namespace
		[]string{"web-app", "api-app"}, // available resources
	)

	// Patch error
	patch := errors.NewPatchError(
		"set",             // operation
		"spec.replicas",   // path
		"my-deployment",   // resource name
		"field not found", // reason
		originalErr,       // cause
	)

	// Parse error with location
	parse := errors.NewParseError(
		"config.yaml",  // source file
		"invalid YAML", // reason
		42,             // line
		10,             // column
		originalErr,    // cause
	)

	// File error
	file := errors.NewFileError("read", "/path/to/file", "permission denied", originalErr)

	// Configuration error
	config := errors.NewConfigError(
		"mise.toml",              // source
		"go",                     // field
		"1.21",                   // value
		"version too old",        // reason
		[]string{"1.23", "1.24"}, // valid values
	)

	for _, err := range []error{validation, notFound, patch, parse, file, config} {
		fmt.Println(err)
	}
}
Output:
invalid replicas for Deployment: -1
Deployment 'my-app' not found in namespace 'default'
patch operation 'set' failed on resource 'my-deployment' at path 'spec.replicas': field not found: original cause
parse error in config.yaml at line 42, column 10: invalid YAML: original cause
file read failed for '/path/to/file': permission denied: original cause
configuration error in mise.toml for field 'go' with value '1.21': version too old

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL