stores

package
v0.8.1 Latest Latest
Warning

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

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

README

pkg/stores

Store provider abstractions and overlay machinery used by the render-validate pipeline to evaluate hypothetical configuration changes without modifying live state.

Overview

The controller's renderer takes a StoreProvider rather than a raw map[string]Store so it can be handed:

  • A RealStoreProvider during normal reconciliation — backed directly by the live pkg/k8s/store instances.
  • An OverlayStoreProvider during webhook validation or proposal-validator runs — wraps the live providers with a ValidationContext of overlays so the proposed change appears in templates without modifying the actual stores.

The package defines a Store interface with the same methods as pkg/k8s/types.Store. Go allows direct assignment between them, preserving the concrete store's optional capabilities, including context-aware reads and immutable snapshots. Separate interfaces keep pkg/stores independent of client-go; arch-go.yml enforces this boundary.

Quick Start

Real provider (production)
import "github.com/haproxy-haptic/HAPTIC/pkg/stores"

real := stores.NewRealStoreProvider(map[string]stores.Store{
    "ingresses": ingressStore,
    "services":  serviceStore,
})

// Renderer uses real.GetStore("ingresses") to fetch the live store.
// (The interface method is GetStore, not Get — Get exists on Store, not on StoreProvider.)
Overlay provider (validation)
overlays := map[string]*stores.StoreOverlay{
    "ingresses": stores.NewStoreOverlayForCreate(proposedIngress),
}
ctx := stores.NewValidationContext(overlays).
    WithHTTPOverlay(httpOverlay) // optional, for HTTP content validation

overlay := stores.NewOverlayStoreProvider(real, ctx)
// overlay.GetStore("ingresses") now returns a *CompositeStore that includes
// proposedIngress on top of the live ingresses store. Stores without a
// matching overlay pass through unchanged.

Overlay Constructors

Constructor Use for
NewStoreOverlay() Empty overlay; populate manually
NewStoreOverlayForCreate(obj) Webhook CREATE — obj appears in the composite store
NewStoreOverlayForUpdate(obj) Webhook UPDATE — obj replaces the existing entry
NewStoreOverlayForDelete(ns, name) Webhook DELETE — entry is hidden in the composite store

The *CompositeStore returned by OverlayStoreProvider.GetStore is read-only — its Add / Update / Delete / Clear methods all return *ReadOnlyStoreError so a template that accidentally tries to write learns immediately rather than corrupting the live store. The provider itself doesn't expose mutation methods at all; the safety comes from the store it hands out.

ContentOverlay vs HTTPContentOverlay

ContentOverlay is the marker interface ("does this overlay carry pending changes?"). HTTPContentOverlay is the same plus three methods for resolving HTTP content lookups during validation. Splitting them lets pkg/stores consume HTTP overlays without importing pkg/httpstore (the latter implements HTTPContentOverlay via its own HTTPOverlay type).

See Also

License

Apache-2.0 — see root LICENSE.

Documentation

Overview

Package stores provides abstractions for accessing and composing resource stores.

Package stores provides abstractions for accessing and composing resource stores.

This package defines:

  • StoreProvider: Interface for accessing stores by name
  • RealStoreProvider: Implementation that returns actual stores
  • ValidationContext: Unified container for K8s and HTTP overlays
  • OverlayStoreProvider: Implementation that applies ValidationContext overlays

These abstractions enable "what if" scenarios by allowing components to render and validate configurations with proposed changes without modifying actual state.

Index

Constants

This section is empty.

Variables

View Source
var ErrIdentityLookupUnsupported = errors.New("exact identity lookup is unsupported")

ErrIdentityLookupUnsupported means an adapter cannot perform an exact identity read.

View Source
var ErrSnapshotChanged = errors.New("store changed after its snapshot was pinned")

ErrSnapshotChanged means a lazy snapshot read could not reproduce its pinned root.

View Source
var ErrSnapshotCommitFenceUnsupported = errors.New("snapshot commit fence is unsupported")
View Source
var ErrSnapshotUnsupported = errors.New("atomic store snapshot is unsupported")

ErrSnapshotUnsupported means a store cannot bind a read to a revision watermark.

Functions

func GetContext

func GetContext(ctx context.Context, store Store, keys ...string) ([]any, error)

GetContext uses the store's context-aware read when available. Legacy reads check cancellation before and after the call but cannot interrupt a read in progress.

func HasIdentityOrderedReads

func HasIdentityOrderedReads(snapshot ReadSnapshot) bool

HasIdentityOrderedReads reports whether collection order is authenticated for the snapshot source.

func ListContext

func ListContext(ctx context.Context, store Store) ([]any, error)

ListContext uses the store's context-aware read or checks cancellation around List.

func SupportsExactRevisionJournal

func SupportsExactRevisionJournal(store Store) bool

SupportsExactRevisionJournal reports whether journal changes are certified complete for the store's immutable snapshot source.

func SupportsSnapshotCommitFence

func SupportsSnapshotCommitFence(store Store) bool

SupportsSnapshotCommitFence reports whether fence calls reach an underlying implementation.

Types

type CompositeStore

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

CompositeStore wraps a base store with an overlay, providing a unified view that reflects both the actual state and proposed changes.

Read operations (Get, List) return the merged view. Write operations are not supported and will return an error.

Thread Safety

CompositeStore itself is not thread-safe, but this is by design:

  • A new CompositeStore is created for each validation request (not shared across goroutines)
  • The base store must be thread-safe (k8s.Store uses sync.RWMutex internally)
  • The overlay must not be modified after construction (immutable during validation)

This design avoids synchronization overhead since each request gets its own instance.

func NewCompositeStore

func NewCompositeStore(base Store, overlay *StoreOverlay) *CompositeStore

NewCompositeStore creates a new CompositeStore.

Parameters:

  • base: The underlying store with actual state
  • overlay: The proposed changes to apply on top

func (*CompositeStore) AcquireSnapshotCommitFence

func (s *CompositeStore) AcquireSnapshotCommitFence(ctx context.Context) (func(), error)

func (*CompositeStore) Add

func (s *CompositeStore) Add(_ any, _ []string) error

Add is not supported on CompositeStore. CompositeStore is read-only; modifications should be made through the overlay.

func (*CompositeStore) ChangesSince

func (s *CompositeStore) ChangesSince(sequence uint64) (uint64, []RevisionChange, bool)

func (*CompositeStore) Clear

func (s *CompositeStore) Clear() error

Clear is not supported on CompositeStore. CompositeStore is read-only; modifications should be made through the overlay.

func (*CompositeStore) Delete

func (s *CompositeStore) Delete(_, _ string, _ []string) error

Delete is not supported on CompositeStore. CompositeStore is read-only; modifications should be made through the overlay.

func (*CompositeStore) ExactRevisionJournalSource

func (s *CompositeStore) ExactRevisionJournalSource() RevisionSource

func (*CompositeStore) Get

func (s *CompositeStore) Get(keys ...string) ([]any, error)

Get returns resources matching the given keys.

The result includes:

  • Resources from the base store (excluding deletions)
  • Modifications (replacing base resources with same keys)
  • Additions that match the keys

func (*CompositeStore) GetContext

func (s *CompositeStore) GetContext(ctx context.Context, keys ...string) ([]any, error)

GetContext returns the overlaid keyed view while propagating cancellation to the base store.

func (*CompositeStore) GetIdentity

func (s *CompositeStore) GetIdentity(namespace, name string) (item any, found bool, err error)

func (*CompositeStore) GetRevision

func (s *CompositeStore) GetRevision(keys ...string) Revision

func (*CompositeStore) GetSnapshot

func (s *CompositeStore) GetSnapshot(
	keys ...string,
) (items []any, revision Revision, sequence uint64, err error)

func (*CompositeStore) IdentityRevision

func (s *CompositeStore) IdentityRevision(namespace, name string) Revision

func (*CompositeStore) IdentitySnapshot

func (s *CompositeStore) IdentitySnapshot(
	namespace, name string,
) (item any, found bool, revision Revision, sequence uint64, err error)

func (*CompositeStore) List

func (s *CompositeStore) List() ([]any, error)

List returns all resources from the merged view.

func (*CompositeStore) ListCached

func (s *CompositeStore) ListCached() ([]any, error)

ListCached returns the overlaid view of any warm base-store entries.

func (*CompositeStore) ListContext

func (s *CompositeStore) ListContext(ctx context.Context) ([]any, error)

ListContext returns the overlaid full view while propagating cancellation to the base store.

func (*CompositeStore) ListRevision

func (s *CompositeStore) ListRevision() Revision

func (*CompositeStore) ListSnapshot

func (s *CompositeStore) ListSnapshot() (items []any, sequence uint64, err error)

func (*CompositeStore) Pin

func (s *CompositeStore) Pin() (ReadSnapshot, error)

Pin delegates empty overlays and rejects overlays without projected index keys.

func (*CompositeStore) RevisionSource

func (s *CompositeStore) RevisionSource() RevisionSource

func (*CompositeStore) Update

func (s *CompositeStore) Update(_ any, _ []string) error

Update is not supported on CompositeStore. CompositeStore is read-only; modifications should be made through the overlay.

type ContentOverlay

type ContentOverlay interface {
	// IsEmpty returns true if the overlay contains no pending changes.
	IsEmpty() bool
}

ContentOverlay is a marker interface for overlay types.

Both K8s overlays (StoreOverlay) and HTTP overlays implement this interface, enabling unified handling in validation pipelines.

type ContextGetter

type ContextGetter interface {
	GetContext(ctx context.Context, keys ...string) ([]any, error)
}

ContextGetter is the optional cancellation-aware Get extension to Store.

type ContextLister

type ContextLister interface {
	ListContext(ctx context.Context) ([]any, error)
}

ContextLister is the optional cancellation-aware List extension to Store.

type ContextReadSnapshot

type ContextReadSnapshot interface {
	ReadSnapshot
	GetContext(ctx context.Context, keys ...string) ([]any, error)
	ListContext(ctx context.Context) ([]any, error)
	GetIdentityContext(ctx context.Context, namespace, name string) (item any, found bool, err error)
}

ContextReadSnapshot cancels reads that can perform external I/O.

type ExactRevisionJournal

type ExactRevisionJournal interface {
	RevisionJournal
	ExactRevisionJournalSource() RevisionSource
}

ExactRevisionJournal guarantees that every retained change carries the exact identity and complete old/new index keys for the named revision source.

type ExactRevisionRangeJournal

type ExactRevisionRangeJournal interface {
	ExactRevisionJournal
	ChangesBetween(from, through uint64) (changes []RevisionChange, complete bool)
}

ExactRevisionRangeJournal certifies both endpoints belong to the same retained history and returns every change between them, including atomic batches.

type ExactSnapshotDiffer

type ExactSnapshotDiffer interface {
	ReadSnapshot
	ChangesFrom(ctx context.Context, previous ReadSnapshot) (changes []RevisionChange, complete bool, err error)
}

ExactSnapshotDiffer proves the complete identity and index-key difference between immutable roots, including divergent branches of one source.

type HTTPContentOverlay

type HTTPContentOverlay interface {
	ContentOverlay

	// GetContent returns content for the given URL.
	// Returns pending content if available, otherwise accepted content.
	GetContent(url string) (string, bool)

	// GetContentForSource returns content only for the matching source authority.
	GetContentForSource(url, sourceIdentity string) (string, bool)

	// PendingURLs returns the list of URLs with pending content.
	PendingURLs() []string

	// HasPendingURL returns true if the given URL has pending content.
	HasPendingURL(url string) bool
}

HTTPContentOverlay represents HTTP content changes awaiting validation.

This interface abstracts the HTTP overlay to avoid circular imports between pkg/stores and pkg/httpstore. The httpstore.HTTPOverlay type implements this.

type IdentityGetter

type IdentityGetter interface {
	GetIdentity(namespace, name string) (resource any, found bool, err error)
}

IdentityGetter retrieves a resource without knowing its configured index keys.

type IdentityOrderedReadSnapshot

type IdentityOrderedReadSnapshot interface {
	ReadSnapshot
	IdentityOrderSource() RevisionSource
}

IdentityOrderedReadSnapshot guarantees List and Get order solely by namespace/name.

type OverlayChange

type OverlayChange struct {
	Namespace string
	Name      string
	Deleted   bool
	Value     any
}

OverlayChange is one immutable proposed identity replacement or deletion.

type OverlayStoreProvider

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

OverlayStoreProvider applies ValidationContext overlays to a base provider.

In addition to applying K8s overlays, this provider also exposes the HTTP overlay for use by the render service.

Usage:

ctx := NewValidationContext(k8sOverlays).WithHTTPOverlay(httpOverlay)
provider := NewOverlayStoreProvider(baseProvider, ctx)

// K8s stores automatically have overlays applied
store := provider.GetStore("ingress")  // Returns CompositeStore if overlay exists

// HTTP overlay is accessible for render service
httpOverlay := provider.GetHTTPOverlay()  // Returns HTTPContentOverlay

func NewOverlayStoreProvider

func NewOverlayStoreProvider(base StoreProvider, validationContext *ValidationContext) *OverlayStoreProvider

NewOverlayStoreProvider creates a provider that applies ValidationContext overlays.

Parameters:

  • base: The underlying store provider
  • validationContext: ValidationContext containing K8s and/or HTTP overlays

If validationContext is nil, behaves identically to the base provider.

func (*OverlayStoreProvider) GetBaseStore

func (p *OverlayStoreProvider) GetBaseStore(name string) Store

GetBaseStore returns the store beneath this request's scratch overlay.

func (*OverlayStoreProvider) GetHTTPOverlay

func (p *OverlayStoreProvider) GetHTTPOverlay() HTTPContentOverlay

GetHTTPOverlay returns the HTTP content overlay from the ValidationContext.

Returns nil if:

  • ValidationContext is nil
  • No HTTP overlay was set

The render service uses this to determine whether to include pending HTTP content.

func (*OverlayStoreProvider) GetK8sOverlay

func (p *OverlayStoreProvider) GetK8sOverlay(name string) *StoreOverlay

GetK8sOverlay returns the immutable scratch overlay for one store.

func (*OverlayStoreProvider) GetStore

func (p *OverlayStoreProvider) GetStore(name string) Store

GetStore returns a store for the given name.

If a K8s overlay exists for this store in the ValidationContext, returns a CompositeStore that applies the overlay on top of the base store. Otherwise, returns the base store unchanged.

func (*OverlayStoreProvider) StoreNames

func (p *OverlayStoreProvider) StoreNames() []string

StoreNames returns the names of all available stores.

func (*OverlayStoreProvider) Validate

func (p *OverlayStoreProvider) Validate() error

Validate checks that all K8s overlays reference valid stores in the base provider.

type ReadOnlyStoreError

type ReadOnlyStoreError struct {
	Operation string
}

ReadOnlyStoreError indicates an attempt to modify a read-only store.

func (*ReadOnlyStoreError) Error

func (e *ReadOnlyStoreError) Error() string

type ReadSnapshot

type ReadSnapshot interface {
	Revisioned
	IdentityGetter
	RevisionSource() RevisionSource
	Sequence() uint64
	Get(keys ...string) ([]any, error)
	List() ([]any, error)
}

ReadSnapshot is one immutable store root. Reads return detached values, and every revision comes from the snapshot's fixed source and sequence.

func OverlayReadSnapshot

func OverlayReadSnapshot(base ReadSnapshot, changes []SnapshotChange) (ReadSnapshot, error)

OverlayReadSnapshot composes projected changes over an immutable base snapshot.

func OverlayReadSnapshotContext

func OverlayReadSnapshotContext(
	ctx context.Context,
	base ReadSnapshot,
	changes []SnapshotChange,
) (ReadSnapshot, error)

OverlayReadSnapshotContext composes an overlay without detaching external reads from ctx.

type RealStoreProvider

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

RealStoreProvider provides access to actual (non-overlaid) stores.

This is the default implementation used during normal reconciliation.

func NewRealStoreProvider

func NewRealStoreProvider(stores map[string]Store) *RealStoreProvider

NewRealStoreProvider creates a new RealStoreProvider with the given stores.

func (*RealStoreProvider) GetStore

func (p *RealStoreProvider) GetStore(name string) Store

func (*RealStoreProvider) StoreNames

func (p *RealStoreProvider) StoreNames() []string

type Revision

type Revision string

Revision is a comparable token for one store read scope. An empty token is unsupported.

type RevisionChange

type RevisionChange struct {
	Sequence  uint64
	Namespace string
	Name      string
	Deleted   bool
	OldKeys   []string
	NewKeys   []string
}

RevisionChange describes one resource-agnostic store mutation.

type RevisionJournal

type RevisionJournal interface {
	ListSnapshot() (items []any, sequence uint64, err error)
	ChangesSince(sequence uint64) (current uint64, changes []RevisionChange, complete bool)
}

RevisionJournal exposes an atomic baseline and bounded changes after it.

type RevisionSource

type RevisionSource uint64

RevisionSource identifies one store instance for its lifetime. Zero is unsupported.

func ExactRevisionJournalSource

func ExactRevisionJournalSource(store Store) RevisionSource

ExactRevisionJournalSource returns the source whose journal contract is exact.

type Revisioned

type Revisioned interface {
	ListRevision() Revision
	GetRevision(keys ...string) Revision
	IdentityRevision(namespace, name string) Revision
}

Revisioned exposes read-scope revisions without loading resource bodies.

type SnapshotChange

type SnapshotChange struct {
	Namespace string
	Name      string
	Deleted   bool
	Value     any
	OldKeys   []string
	NewKeys   []string
}

SnapshotChange is one identity replacement or deletion with its projected keys.

type SnapshotCommitFencer

type SnapshotCommitFencer interface {
	AcquireSnapshotCommitFence(ctx context.Context) (release func(), err error)
}

type SnapshotCommitMutex

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

func (*SnapshotCommitMutex) Acquire

func (m *SnapshotCommitMutex) Acquire(ctx context.Context) (func(), error)

func (*SnapshotCommitMutex) Lock

func (m *SnapshotCommitMutex) Lock()

func (*SnapshotCommitMutex) LockContext

func (m *SnapshotCommitMutex) LockContext(ctx context.Context) error

func (*SnapshotCommitMutex) Unlock

func (m *SnapshotCommitMutex) Unlock()

type SnapshotProvider

type SnapshotProvider interface {
	Pin() (ReadSnapshot, error)
}

SnapshotProvider pins an immutable store root without loading its resources.

type SnapshotReader

type SnapshotReader interface {
	RevisionSource() RevisionSource
	GetSnapshot(keys ...string) (items []any, revision Revision, sequence uint64, err error)
	IdentitySnapshot(namespace, name string) (
		item any, found bool, revision Revision, sequence uint64, err error,
	)
}

SnapshotReader binds exact read results to their revision and journal watermark.

type Store

type Store interface {
	// Get retrieves all resources matching the provided index keys.
	Get(keys ...string) ([]any, error)

	// List returns all resources in the store.
	List() ([]any, error)

	// Add inserts a new resource into the store with the provided index keys.
	Add(resource any, keys []string) error

	// Update modifies an existing resource in the store.
	Update(resource any, keys []string) error

	// Delete removes the single resource identified by namespace/name. Index
	// keys need not be unique, so identity is authoritative and siblings remain.
	Delete(namespace, name string, keys []string) error

	// Clear removes all resources from the store.
	Clear() error
}

Store defines the interface for storing and retrieving indexed resources.

This interface mirrors pkg/k8s/Store but is defined here to maintain package independence. Go's implicit interface satisfaction means any type implementing these methods (like k8s/Store) automatically satisfies this.

type StoreOverlay

type StoreOverlay struct {
	// Additions are new resources to add to the store.
	Additions []runtime.Object

	// Modifications are existing resources with updated content.
	Modifications []runtime.Object

	// Deletions are keys identifying resources to remove.
	// Uses NamespacedName for consistent key format.
	Deletions []ktypes.NamespacedName
	// contains filtered or unexported fields
}

StoreOverlay represents proposed changes to a store.

This enables "what if" scenarios: validate configurations with proposed changes without modifying the actual store state.

Resources are pre-converted to template-friendly format (floats to ints) at construction time to avoid repeated conversion during Get()/List() calls.

func NewStoreOverlay

func NewStoreOverlay() *StoreOverlay

NewStoreOverlay creates a new empty StoreOverlay.

func NewStoreOverlayForCreate

func NewStoreOverlayForCreate(obj runtime.Object) *StoreOverlay

NewStoreOverlayForCreate creates a StoreOverlay for a CREATE operation. The resource is pre-converted to template-friendly format.

func NewStoreOverlayForDelete

func NewStoreOverlayForDelete(namespace, name string) *StoreOverlay

NewStoreOverlayForDelete creates a StoreOverlay for a DELETE operation.

func NewStoreOverlayForUpdate

func NewStoreOverlayForUpdate(obj runtime.Object) *StoreOverlay

NewStoreOverlayForUpdate creates a StoreOverlay for an UPDATE operation. The resource is pre-converted to template-friendly format.

func (*StoreOverlay) AddAddition

func (o *StoreOverlay) AddAddition(obj runtime.Object)

AddAddition adds a resource to the additions list and pre-converts it. Use this method instead of directly modifying the Additions field to ensure the pre-converted slice stays in sync.

func (*StoreOverlay) AddModification

func (o *StoreOverlay) AddModification(obj runtime.Object)

AddModification adds a resource to the modifications list and pre-converts it. Use this method instead of directly modifying the Modifications field to ensure the pre-converted slice stays in sync.

func (*StoreOverlay) IdentityChanges

func (o *StoreOverlay) IdentityChanges() ([]OverlayChange, bool)

IdentityChanges returns a deterministic snapshot and false for ambiguous overlays.

func (*StoreOverlay) IsEmpty

func (o *StoreOverlay) IsEmpty() bool

IsEmpty returns true if the overlay contains no changes.

type StoreProvider

type StoreProvider interface {
	// GetStore returns the store for the given name.
	// Returns nil if no store exists with that name.
	GetStore(name string) Store

	// StoreNames returns the names of all available stores.
	StoreNames() []string
}

StoreProvider provides access to stores by name.

This abstraction decouples consumers from how stores are obtained, enabling composition and overlaying of proposed changes.

type ValidationContext

type ValidationContext struct {
	// K8sOverlays maps store names to their proposed changes.
	K8sOverlays map[string]*StoreOverlay

	// HTTPOverlay contains pending HTTP content changes.
	// Nil means no HTTP content changes to validate.
	HTTPOverlay HTTPContentOverlay
}

ValidationContext is a unified container for all overlay types.

It holds both K8s store overlays and HTTP content overlays, enabling the render pipeline to apply proposed changes transparently during validation.

Usage:

// For K8s-only validation (webhook admission)
ctx := NewValidationContext(k8sOverlays)

// For HTTP content validation
ctx := NewValidationContext(nil).WithHTTPOverlay(httpOverlay)

// For combined validation
ctx := NewValidationContext(k8sOverlays).WithHTTPOverlay(httpOverlay)

func NewValidationContext

func NewValidationContext(overlays map[string]*StoreOverlay) *ValidationContext

NewValidationContext creates a ValidationContext with K8s overlays.

Parameters:

  • overlays: Map of store name to proposed K8s changes (can be nil)

Returns a ValidationContext ready for use or further configuration.

func (*ValidationContext) IsEmpty

func (c *ValidationContext) IsEmpty() bool

IsEmpty returns true if the context contains no overlays.

A context is empty if it has no K8s overlays AND no HTTP overlay.

func (*ValidationContext) WithHTTPOverlay

func (c *ValidationContext) WithHTTPOverlay(overlay HTTPContentOverlay) *ValidationContext

WithHTTPOverlay adds an HTTP overlay to the validation context.

Returns the same ValidationContext for method chaining:

ctx := NewValidationContext(overlays).WithHTTPOverlay(httpOverlay)

Directories

Path Synopsis
Package storetest provides shared mock implementations of stores.Store for testing.
Package storetest provides shared mock implementations of stores.Store for testing.

Jump to

Keyboard shortcuts

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