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
- type Store
- func (r *Store) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error)
- func (r *Store) Create(ctx context.Context, obj runtime.Object, ...) (runtime.Object, error)
- func (r *Store) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, ...) (runtime.Object, bool, error)
- func (r *Store) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, ...) (runtime.Object, error)
- func (r *Store) Destroy()
- func (r *Store) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error)
- func (r *Store) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set
- func (r *Store) GetSingularName() string
- func (r *Store) KeyFunc(ctx context.Context, name string) (string, error)
- func (r *Store) KeyRootFunc(ctx context.Context) string
- func (r *Store) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error)
- func (r *Store) NamespaceScoped() bool
- func (r *Store) New() runtime.Object
- func (r *Store) NewList() runtime.Object
- func (r *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, ...) (runtime.Object, bool, error)
- func (r *Store) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
- type Strategy
Constants ¶
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.
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 (*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) 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 (*Store) KeyFunc ¶
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 ¶
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 ¶
func (r *Store) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error)
List propagates Limit/Continue/ResourceVersionMatch (checklist item 12), mirroring genericregistry.Store.ListPredicate (vendor store.go:389-425).
func (*Store) NamespaceScoped ¶
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 ¶
func (r *Store) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
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.