indexer

package
v0.2.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

pkg/k8s/indexer

Extracts composite index keys from Kubernetes resources via JSONPath expressions, and (optionally) strips fields the controller doesn't care about to reduce memory pressure.

Overview

*Indexer combines two concerns: turning a watched resource into the slice of strings the store uses as a lookup key, and removing noisy fields (e.g. metadata.managedFields) from the in-memory copy. Both phases are configured with JSONPath expressions, evaluated up-front for fail-fast validation.

This is what the controller wires when it consumes the CRD's indexBy and watchedResourcesIgnoreFields.

Quick Start

import "gitlab.com/haproxy-haptic/haptic/pkg/k8s/indexer"

idx, err := indexer.New(indexer.Config{
    IndexBy: []string{
        "metadata.namespace",
        "metadata.name",
    },
    IgnoreFields: []string{
        "metadata.managedFields",
    },
})
if err != nil { /* invalid JSONPath -- fail at startup */ }

keys, err := idx.ExtractKeys(ingress)         // ["default", "my-ingress"]
err = idx.FilterFields(ingress)               // mutates in place

ExtractKeys returns the keys in the same order as the IndexBy slice, so a store can build a default/my-ingress composite key (or accept partial-prefix lookups against just default). FilterFields mutates the resource in place — feed it the same object you'll hand to the store afterwards.

Helpers

  • NewJSONPathEvaluator(expr) — parses a single expression up-front, lets callers cache it.
  • NewFieldSelectorMatcher(expr) — used by watchers that take a fieldSelector and need to match resources locally.
  • Errors are typed (*IndexError from ExtractKeys, *FilterError from FilterFields, *JSONPathError from NewJSONPathEvaluator) so callers can extract the expression / pattern that failed without string-matching the message. All three implement Unwrap() so errors.Is / errors.As walks through to the underlying cause.

See Also

License

Apache-2.0 — see root LICENSE.

Documentation

Overview

Package indexer provides functionality for extracting index keys from Kubernetes resources and filtering fields based on JSONPath expressions.

This package is used by the store implementations to: - Extract index keys for O(1) lookups - Remove unnecessary fields to reduce memory usage

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConvertResource

func ConvertResource(resource any) map[string]any

ConvertResource converts an unstructured resource to a map with floats converted to ints.

This function is used during resource storage to ensure all resources are stored in a template-friendly format. The conversion happens once when resources are added/updated in stores, rather than on every access during template rendering.

The returned map can be used directly in templates for field access like:

resource["metadata"]["name"]
resource["spec"]["rules"]

Returns nil if the resource type is not supported.

func RootField

func RootField(pattern string) string

RootField returns the top-level (first) field segment of a JSONPath expression, or "" if the pattern is empty. It is resource-agnostic — it only parses the JSONPath string — and is used to compute the set of top-level object fields a projection must retain so that index-key extraction and field-selector evaluation still work on the projected object.

Examples:

  • "metadata.name" -> "metadata"
  • "metadata.labels['kubernetes.io/x']" -> "metadata"
  • "spec.rules[0].host" -> "spec"
  • "status" -> "status"

func ValidateJSONPath

func ValidateJSONPath(expr string) error

ValidateJSONPath validates a JSONPath expression syntax without executing it.

This is a generic validation function that can be used anywhere JSONPath validation is needed. It only checks syntax correctness and does not require a resource to evaluate against.

The expression should follow JSONPath syntax without the surrounding braces. For example: "metadata.namespace" or "metadata.labels['app']"

Parameters:

  • expr: The JSONPath expression to validate

Returns:

  • An error if the expression syntax is invalid
  • nil if the expression is valid

Example:

err := indexer.ValidateJSONPath("metadata.namespace")
if err != nil {
    slog.Error("Invalid JSONPath", "error", err)
}

Types

type Config

type Config struct {
	// IndexBy specifies JSONPath expressions for extracting index keys.
	// At least one expression is required.
	IndexBy []string

	// IgnoreFields specifies JSONPath patterns for fields to remove.
	// These fields are removed from resources before storage.
	IgnoreFields []string
}

Config configures the indexer behavior.

type FieldFilter

type FieldFilter struct {
	// contains filtered or unexported fields
}

FieldFilter removes fields from Kubernetes resources based on JSONPath expressions.

This is used to reduce memory usage by removing unnecessary fields before storing resources in the index (e.g., metadata.managedFields).

func NewFieldFilter

func NewFieldFilter(patterns []string) *FieldFilter

For example: "metadata.managedFields", "metadata.annotations".

func (*FieldFilter) Filter

func (f *FieldFilter) Filter(resource any) error

Filter removes matching fields from the resource.

The resource is modified in-place for efficiency. Returns an error if filtering fails.

Example:

filter := NewFieldFilter([]string{"metadata.managedFields"})
err := filter.Filter(resource)

type FieldSelectorMatcher

type FieldSelectorMatcher struct {
	// contains filtered or unexported fields
}

FieldSelectorMatcher filters resources by evaluating a field selector expression. It parses expressions in the format "field.path=value" and uses client-side JSONPath evaluation to determine if a resource matches.

func NewFieldSelectorMatcher

func NewFieldSelectorMatcher(expression string) (*FieldSelectorMatcher, error)

NewFieldSelectorMatcher creates a new matcher for the given field selector expression.

The expression must be in the format "field.path=value" where:

  • field.path is a JSONPath expression (e.g., "spec.ingressClassName")
  • value is the expected string value (e.g., "haproxy-internal")

Example: "spec.ingressClassName=haproxy-internal"

Returns an error if the expression format is invalid or the JSONPath is malformed.

func (*FieldSelectorMatcher) Matches

func (m *FieldSelectorMatcher) Matches(resource any) (bool, error)

Matches returns true if the resource matches the field selector expression.

Returns:

  • (true, nil) if the field exists and its value matches the expected value
  • (false, nil) if the field doesn't exist or the value doesn't match
  • (false, error) only for unexpected evaluation errors

A missing field is treated as a non-match (not an error), allowing resources without the field to be filtered out gracefully.

type FilterError

type FilterError struct {
	Pattern string
	Cause   error
}

FilterError represents an error during field filtering.

func (*FilterError) Error

func (e *FilterError) Error() string

func (*FilterError) Unwrap

func (e *FilterError) Unwrap() error

type IndexError

type IndexError struct {
	Expression string
	Position   int
	Cause      error
}

IndexError represents an error during index key extraction.

func (*IndexError) Error

func (e *IndexError) Error() string

func (*IndexError) Unwrap

func (e *IndexError) Unwrap() error

type Indexer

type Indexer struct {
	// contains filtered or unexported fields
}

Indexer extracts index keys from Kubernetes resources and filters fields.

It combines JSONPath evaluation for key extraction with field filtering for memory optimization.

func New

func New(cfg Config) (*Indexer, error)

New creates a new Indexer with the provided configuration.

Returns an error if:

  • IndexBy is empty
  • Any JSONPath expression is invalid (fail-fast)

Example:

indexer, err := indexer.New(indexer.Config{
    IndexBy: []string{
        "metadata.namespace",
        "metadata.name",
    },
    IgnoreFields: []string{
        "metadata.managedFields",
    },
})

func (*Indexer) ExtractKeys

func (idx *Indexer) ExtractKeys(resource any) ([]string, error)

ExtractKeys extracts index keys from the resource using configured JSONPath expressions.

Returns:

  • A slice of string keys in the order of IndexBy configuration
  • An error if key extraction fails

Example:

// For IndexBy: ["metadata.namespace", "metadata.name"]
keys, err := indexer.ExtractKeys(ingress)
// keys = ["default", "my-ingress"]

func (*Indexer) FilterFields

func (idx *Indexer) FilterFields(resource any) error

FilterFields removes fields from the resource based on IgnoreFields configuration.

The resource is modified in-place for efficiency. Returns an error if filtering fails.

Example:

err := indexer.FilterFields(ingress)
// ingress.Metadata.ManagedFields is now removed

func (*Indexer) NumKeys

func (idx *Indexer) NumKeys() int

NumKeys returns the number of index keys this indexer extracts.

func (*Indexer) Process

func (idx *Indexer) Process(resource any) (*ProcessResult, error)

Process filters fields, extracts keys, and converts the resource for template use.

This is the most common usage pattern: filter the resource to reduce memory, extract keys for indexing, and convert for template access.

Returns:

  • ProcessResult containing index keys and converted resource
  • An error if any operation fails

The original resource is modified in-place by field filtering.

Example:

result, err := indexer.Process(ingress)
if err != nil {
    return err
}
store.Add(result.ConvertedResource, result.Keys)

type JSONPathError

type JSONPathError struct {
	Expression string
	Operation  string
	Cause      error
}

JSONPathError represents an error during JSONPath evaluation.

func (*JSONPathError) Error

func (e *JSONPathError) Error() string

func (*JSONPathError) Unwrap

func (e *JSONPathError) Unwrap() error

type JSONPathEvaluator

type JSONPathEvaluator struct {
	// contains filtered or unexported fields
}

JSONPathEvaluator evaluates JSONPath expressions against Kubernetes resources.

func NewJSONPathEvaluator

func NewJSONPathEvaluator(expression string) (*JSONPathEvaluator, error)

NewJSONPathEvaluator creates a new JSONPath evaluator for the given expression.

The expression should follow JSONPath syntax without the surrounding braces. For example: "metadata.namespace" or "metadata.labels['app']"

Returns an error if the expression is invalid (fail-fast).

func (*JSONPathEvaluator) Evaluate

func (e *JSONPathEvaluator) Evaluate(resource any) (string, error)

Evaluate executes the JSONPath expression against the provided resource.

Returns:

  • The extracted value as a string
  • An error if evaluation fails or the result is not a string

If the expression matches multiple values, only the first is returned.

func (*JSONPathEvaluator) Expression

func (e *JSONPathEvaluator) Expression() string

Expression returns the JSONPath expression used by this evaluator.

type ProcessResult

type ProcessResult struct {
	// Keys are the index keys for store lookups.
	Keys []string

	// ConvertedResource is the resource with floats converted to ints,
	// ready for use in templates. This is stored instead of the original
	// unstructured.Unstructured to avoid conversion on every access.
	ConvertedResource map[string]any
}

ProcessResult contains the results of processing a resource for storage.

Jump to

Keyboard shortcuts

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