genericrest

package
v0.0.340 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package genericrest provides a reusable, hand-written rest.Storage implementation that talks directly to a shared storage.Interface (this repo's StorageImpl), as a parallel, per-resource-gated alternative to k8s.io/apiserver's etcd-shaped genericregistry.Store.

It is deliberately configured the same way genericregistry.Store itself is -- a struct with function-value and interface-typed fields (NewFunc, NewListFunc, PredicateFunc, Strategy, ...), NOT Go generic type parameters -- so it can be shared across resource packages without fighting generics for interface-typed parameters. Each resource package (e.g. pkg/registry/softwarecomposition/knownservers, .../openvulnerabilityexchange) provides only its own thin, type-specific configuration (New/NewList, key-func choice via Strategy.NamespaceScoped, predicate, qualified resource names, strategy value) and gets the rest of this behavior for free.

This is the Phase 4 deliverable described in docs/features/generic-rest-storage-phase4.md: most of what a genericregistry.Store-based NewREST provides for free is actually resource-agnostic once objects are manipulated via meta.Accessor reflection -- Delete, DeleteCollection, markAsDeleting, finalizeDeleteStatus, and the Update/tryUpdate closure's BeforeUpdate/conflict/dry-run logic all generalize directly. Only the handful of genuinely resource-specific pieces are left to each resource package: New()/NewList(), the key-function choice (cluster-scoped vs. namespaced, chosen the same way genericregistry.Store.CompleteWithOptions does, vendor store.go:1514-1586), the label/field selector predicate function, the qualified resource names, and the strategy value itself.

Index

Constants

View Source
const DefaultDeleteCollectionPageSize = 500

DefaultDeleteCollectionPageSize bounds how many items DeleteCollection deletes per internal List call when the caller didn't request a specific page size. This is deliberately DeleteCollection's own constant, not derived from List's behavior: List (StorageImpl.GetList) used to impose an implicit page size of 500 on any unpaginated call, and DeleteCollection's pagination loop below was silently relying on that -- its only cancellation checkpoint is the ctx.Done() check at the top of the loop, which used to run roughly every 500 deleted items as a side effect of List's old default. Once List was fixed to return everything in one call when no Limit is given (see docs/features/getlist-unset-limit-returns-everything.md), that implicit checkpoint disappeared: a "delete all" call with no explicit Limit would list and then delete an entire large collection in one uninterruptible pass. DeleteCollection now requests its own bounded page explicitly, independent of whatever List's own default happens to be.

View Source
const OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again"

OptimisticLockErrorMsg matches genericregistry.OptimisticLockErrorMsg (vendor store.go:262) so a resourceVersion conflict produces the same error message old and new implementations agree on.

Variables

This section is empty.

Functions

This section is empty.

Types

type Store

type Store struct {
	// NewFunc returns a new, empty instance of the resource's concrete type.
	NewFunc func() runtime.Object
	// NewListFunc returns a new, empty instance of the resource's concrete
	// list type.
	NewListFunc func() runtime.Object
	// PredicateFunc builds the label/field selector predicate used by
	// List/Watch/DeleteCollection, analogous to genericregistry.Store's
	// PredicateFunc field.
	PredicateFunc func(label labels.Selector, field fields.Selector) storage.SelectionPredicate

	// DefaultQualifiedResource and SingularQualifiedResource are this
	// resource's plural/singular GroupResource, used for error messages,
	// GetSingularName, and RESTOptionsGetter.GetRESTOptions.
	DefaultQualifiedResource  schema.GroupResource
	SingularQualifiedResource schema.GroupResource

	// Strategy carries the resource's create/update validation, defaulting,
	// and scope behavior -- see the Strategy interface above.
	Strategy Strategy

	// ResetFieldsStrategy is optional (nil by default, matching every
	// resource strategy in this repo today, none of which implements
	// rest.ResetFieldsStrategy) -- see GetResetFields, which reproduces
	// genericregistry.Store.GetResetFields (vendor store.go:1698-1704)
	// exactly, including its nil-when-unset behavior.
	ResetFieldsStrategy rest.ResetFieldsStrategy

	// TableConvertor renders ConvertToTable responses. If left nil,
	// NewStore defaults it to rest.NewDefaultTableConvertor(DefaultQualifiedResource)
	// (name + age columns only), matching every resource's current
	// // TODO: define table converter ... in its etcd.go.
	TableConvertor rest.TableConvertor

	// Storage is the shared storage.Interface (this repo's StorageImpl)
	// this Store reads/writes through. Set by NewStore.
	Storage storage.Interface
	// contains filtered or unexported fields
}

Store is a hand-written rest.Storage implementation, generic over any resource type via meta.Accessor reflection and the function/interface values below -- mirroring exactly how genericregistry.Store itself is configured (NewFunc, NewListFunc, PredicateFunc, Strategy fields, etc.), not Go generic type parameters.

A resource package constructs one of these via NewStore with its own type-specific configuration; everything else (Create/Update/Delete/List/ Watch/DeleteCollection and all their generic.Store-equivalent behaviors) is provided here, once, for every resource that uses it.

func NewStore

func NewStore(storageImpl storage.Interface, optsGetter generic.RESTOptionsGetter, cfg Store) (*Store, error)

NewStore completes a partially-configured Store (NewFunc, NewListFunc, PredicateFunc, DefaultQualifiedResource, SingularQualifiedResource, Strategy, and optionally ResetFieldsStrategy/TableConvertor set by the caller) by deriving its on-disk key prefix from optsGetter, the same RESTOptionsGetter the OLD genericregistry.Store-based NewREST for the same resource is wired with (see pkg/apiserver/apiserver.go's `ep` helper), and wiring in storageImpl. It returns a new, independent *Store -- the cfg value passed in is not mutated.

func (*Store) ConvertToTable

func (r *Store) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error)

func (*Store) Create

func (r *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error)

Create implements checklist items 1 (UID/creationTimestamp stamping), 2 (generateName + bounded retry), and 7 (dry-run).

func (*Store) Delete

func (r *Store) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error)

Delete implements checklist items 6 (finalizer/graceful-deletion state machine) and 7 (dry-run). See knownservers/custom_rest.go's original comment (preserved in git history) for the full reasoning this mirrors -- graceful deletion and GC-finalizer injection never trigger for any resource in this repo (no RESTGracefulDeleteStrategy implementations, EnableGarbageCollection=false everywhere), so what remains is: an object with existing finalizers gets deletionTimestamp/deletionGracePeriodSeconds set via GuaranteedUpdate rather than hard-deleted; hard delete happens later via a subsequent Update that empties the finalizers list (handled by deleteWithoutFinalizers) or immediately here if there were no finalizers.

func (*Store) DeleteCollection

func (r *Store) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error)

DeleteCollection implements checklist item 10. Unlike genericregistry.Store.DeleteCollection (vendor store.go:1237-1382), this is a simple sequential implementation (no worker pool) -- every resource using this package so far is low-traffic enough that a bulk delete is not expected to need parallelism. It does still paginate through List's continue token like vendor does (vendor store.go:1298-1362): silently stopping at one page would delete only part of the collection while reporting success, and pagination is also what keeps the ctx.Done() check below effective on a large collection (see DefaultDeleteCollectionPageSize). As in vendor, a caller-supplied explicit Limit is honored as a request for just that one page rather than paginated through. Every matched object is still individually deleted through the same Delete path above (so finalizers/dry-run/etc. behave identically per-item), and the returned list mirrors everything that was deleted across all pages.

func (*Store) Destroy

func (r *Store) Destroy()

Destroy is a deliberate no-op: Store does not own the lifecycle of the shared StorageImpl/pool passed in by apiserver.go (it is shared with the old NewREST-based implementation and other resources), so there is nothing for this specific rest.Storage to clean up on shutdown.

func (*Store) Get

func (r *Store) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error)

func (*Store) GetResetFields

func (r *Store) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set

GetResetFields implements rest.ResetFieldsStrategy, reproducing genericregistry.Store.GetResetFields (vendor store.go:1698-1704) exactly: nil whenever no ResetFieldsStrategy is configured, which is every resource strategy in this repo today.

func (*Store) GetSingularName

func (r *Store) GetSingularName() string

func (*Store) KeyFunc

func (r *Store) KeyFunc(ctx context.Context, name string) (string, error)

KeyFunc chooses between genericregistry.NamespaceKeyFunc and genericregistry.NoNamespaceKeyFunc based on r.Strategy.NamespaceScoped(), exactly the same conditional genericregistry.Store.CompleteWithOptions uses to pick a KeyFunc (vendor store.go:1514-1586) -- reproduced by calling the same exported vendor functions directly rather than re-deriving their path.IsValidPathSegmentName guards.

func (*Store) KeyRootFunc

func (r *Store) KeyRootFunc(ctx context.Context) string

KeyRootFunc mirrors genericregistry.Store's KeyRootFunc field the same way: for a namespaced resource it's the prefix plus the request's namespace (when one is present in ctx); for a cluster-scoped resource it's the bare prefix (vendor store.go:1569-1586).

func (*Store) List

List propagates Limit/Continue/ResourceVersionMatch (checklist item 12), mirroring genericregistry.Store.ListPredicate (vendor store.go:389-425).

func (*Store) NamespaceScoped

func (r *Store) NamespaceScoped() bool

func (*Store) New

func (r *Store) New() runtime.Object

func (*Store) NewList

func (r *Store) NewList() runtime.Object

func (*Store) Update

func (r *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error)

Update implements checklist items 3 (BeforeUpdate carry-over rules), 4 (resourceVersion conflict -> 409), 5 (re-invocation of update transformation/admission on retry), and 7 (dry-run).

Finding for checklist item 5: the tryUpdate closure is passed by reference into storage.Interface.GuaranteedUpdate exactly once; it does NOT need a separate retry loop here, because StorageImpl.GuaranteedUpdate's own retry loop already re-invokes the *same* closure -- including objInfo.UpdatedObject (the admission/transformation step) and createValidation/updateValidation -- on every precondition-check-failed or stale-tryUpdate-error retry.

func (*Store) Watch

Watch propagates AllowWatchBookmarks/SendInitialEvents (checklist item 13), mirroring genericregistry.Store.Watch/WatchPredicate (vendor store.go:1417-1463). Whether the underlying StorageImpl.Watch actually honors these fields is unchanged by this migration either way -- both old and new implementations pass through to the exact same StorageImpl.Watch.

type Strategy

type Strategy interface {
	runtime.ObjectTyper
	names.NameGenerator

	NamespaceScoped() bool
	PrepareForCreate(ctx context.Context, obj runtime.Object)
	Validate(ctx context.Context, obj runtime.Object) field.ErrorList
	WarningsOnCreate(ctx context.Context, obj runtime.Object) []string
	Canonicalize(obj runtime.Object)
	AllowCreateOnUpdate() bool
	PrepareForUpdate(ctx context.Context, obj, old runtime.Object)
	ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList
	WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string
	AllowUnconditionalUpdate() bool
}

Strategy is the minimal interface this package needs from a resource's strategy value. It is a superset of rest.RESTCreateStrategy and rest.RESTUpdateStrategy (which have overlapping but not identical method sets), so any concrete strategy that already satisfies both -- as every resource strategy in this repo does -- also satisfies this interface, and a Strategy value passed as either of those narrower interfaces to rest.BeforeCreate/rest.BeforeUpdate/rest.BeforeDelete works exactly as it would against genericregistry.Store.

Jump to

Keyboard shortcuts

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