Documentation
¶
Overview ¶
Package scale defines the decentralized scaling interfaces described in the README's "Scaling design" chapter. L0 (API hygiene) and L1 (claim ownership transfer) are implemented in the vk-cocoon provider and the extensions controllers respectively; this package holds the L2 and L3 contracts:
ClaimGateway (L2): the node-local claim fast path over sandboxd. A claim is served by the node that already holds a warm microVM; the SandboxClaim object is reconciled to Bound asynchronously afterward (kubelet static-Pod semantics: the node acts first, the apiserver records after).
SandboxStore + NodeInventory (L3): the aggregated-apiserver storage contract. sandboxes.agents.x-k8s.io is served by scatter-gathering live node inventories; etcd stores only intent (warm-pool desired replicas plus one O(nodes) NodeInventory object per node), the metrics.k8s.io pattern.
Both contracts are implemented here: the sandboxd-backed ClaimGateway and its orphan reconciler (claimgateway_impl.go), and the scatter-gather store with its NodeInventory publisher and cache-fed inventory source (sandboxstore_impl.go), served by cmd/sandbox-apiserver via pkg/scale/apiserver.
Index ¶
- Constants
- Variables
- func AddressIPs(addr string) []string
- func IsFallback(err error) bool
- func IsNoWarmCapacity(err error) bool
- func NewGateway(cfg GatewayConfig) *nodeClaimGateway
- func NewScatterGatherStore(src InventorySource, opts ...StoreOption) *scatterGatherStore
- func SizeClassForContainers(containers []corev1.Container) string
- type Assignment
- type Authorizer
- type ClaimGateway
- type ClaimRecorder
- type ClaimRequest
- type ClientInventorySource
- type Delivery
- type GatewayConfig
- type InventoryApplier
- type InventoryEntry
- type InventorySource
- type ListOptions
- type NodeInventory
- type NodeInventoryPublisher
- type NodeInventorySource
- type NodeLiveSource
- type OrphanReconciler
- type PoolCapacity
- type PoolKey
- type ReviewAuthorizer
- type SandboxLifecycle
- type SandboxStats
- type SandboxStore
- type SandboxdClient
- type SandboxdClientFactory
- type Snapshot
- type StaticInventorySource
- func (s *StaticInventorySource) Apply(_ context.Context, inv *NodeInventory) error
- func (s *StaticInventorySource) ApplyCount() int
- func (s *StaticInventorySource) ListNodes(_ context.Context) ([]string, error)
- func (s *StaticInventorySource) NodeInventory(_ context.Context, node string) (*NodeInventory, error)
- func (s *StaticInventorySource) ObjectCount() int
- func (s *StaticInventorySource) Partition(node string)
- func (s *StaticInventorySource) Put(inv *NodeInventory)
- func (s *StaticInventorySource) Remove(node string)
- type StoreOption
- type SubjectAccessReviewer
Constants ¶
const ( // Selector keys a ClaimRequest may carry to override the ClaimSpec axes. SelectorTemplateKey = "sandbox.cocoonstack.io/template" SelectorNetKey = "sandbox.cocoonstack.io/net" SelectorSizeKey = "sandbox.cocoonstack.io/size" // BoundConditionType marks a SandboxClaim whose warm sandbox has been delivered. BoundConditionType = "Bound" // RequestUserSelectorKey names the Selector entry carrying the caller identity the // default Authorizer evaluates in its SubjectAccessReview. Absent, the default // Authorizer fails closed. RequestUserSelectorKey = "authorization.cocoonstack.io/user" )
const ( SizeClassSmall = "small" SizeClassMedium = "medium" SizeClassLarge = "large" // NetDefault is the mode used when none is annotated: the NIC-less Firecracker lane. NetDefault = "none" // NetEgress is the egress-capable network lane. NetEgress = "egress" )
const ( // Synthesized-Sandbox label keys. The aggregated store stamps these onto every // Sandbox it materializes from a NodeInventory entry so label selectors (the // `kubectl get sandboxes -l ...` path) have real axes to filter on without any // per-sandbox etcd object. // NodeLabel carries the owning node of a synthesized Sandbox. NodeLabel = "sandbox.cocoonstack.io/node" // PhaseLabel carries the entry phase of a synthesized Sandbox. PhaseLabel = "sandbox.cocoonstack.io/phase" // ClaimLabel carries the claim name a synthesized Sandbox is bound to. ClaimLabel = "sandbox.cocoonstack.io/claim" // ClaimIDAnnotation carries the owning node's sandboxd claim id ("sb_...") on a // synthesized Sandbox. Unlike the label keys above it is an annotation — an // opaque node-local handle, not a selector axis: the aggregated apiserver reads // it on Delete to release exactly the microVM this Sandbox stands for (releasing // by k8s name would target the wrong claim). This is the single definition of // the key; apiserver.ClaimIDAnnotation aliases it so both write it identically. ClaimIDAnnotation = "sandbox.cocoonstack.io/claim-id" )
Variables ¶
var ( NodeInventoryGVK = extv1beta1.GroupVersion.WithKind("NodeInventory") // ErrNoWarmCapacity lets the aggregated apiserver map an exhausted pool to a retryable 503 instead of writing an object. ErrNoWarmCapacity = errors.New("scale: no node has warm capacity for the requested pool") )
NodeInventoryGVK is the GroupVersionKind of the O(nodes) intent object the publisher server-side-applies. It lives in the extensions CRD group next to SandboxClaim/Template/WarmPool — NOT in the aggregated agents.x-k8s.io group: the APIService hands that entire group-version to the aggregated server, which serves only `sandboxes`, so a NodeInventory registered there would 404 once the APIService cuts over.
var ErrNoNodeCapacity = errors.New("scale: node has no warm capacity; fall back to L1 claim path")
ErrNoNodeCapacity is the sentinel Claim returns when the node has no warm VM to hand over (sandboxd answered 429/draining, or redirected to a peer). The caller falls back to the L1 Kubernetes path (create a new Sandbox). Test it with IsFallback rather than comparing directly, so future wrapped causes still match.
Functions ¶
func AddressIPs ¶
AddressIPs strips the port from a "host:port" address, yielding the pod IP list a synthesized Sandbox status carries. Shared with the aggregated apiserver so both stamp identical PodIPs.
func IsFallback ¶
IsFallback reports whether err means the L2 node-local fast path declined and the caller should fall back to the L1 Kubernetes claim path.
func IsNoWarmCapacity ¶
IsNoWarmCapacity reports whether err means Claim found no warm node.
func NewGateway ¶
func NewGateway(cfg GatewayConfig) *nodeClaimGateway
NewGateway builds a node-local ClaimGateway from cfg. It returns the concrete type so callers can Wait on async record drain during graceful shutdown; the value satisfies ClaimGateway.
func NewScatterGatherStore ¶
func NewScatterGatherStore(src InventorySource, opts ...StoreOption) *scatterGatherStore
NewScatterGatherStore builds the aggregated store over src.
func SizeClassForContainers ¶
SizeClassForContainers maps the first container's CPU/memory onto small|medium| large. It prefers requests, falls back to limits, and defaults to "small" when neither is set. Thresholds: >4 CPU or >8Gi -> large; >1 CPU or >2Gi -> medium.
Types ¶
type Assignment ¶
type Assignment struct {
SandboxName string
Node string
Address string
// Token is the per-sandbox ownership credential returned by sandboxd on the
// claim. It authenticates agent/exec against the delivered VM; the L3 Create
// path surfaces it as an annotation so a caller can exec into what it claimed.
Token string
}
Assignment is the result of a successful claim: the warm sandbox whose ownership was transferred, the node serving it, and its connection address. The SandboxClaim is marked Bound asynchronously after this returns.
type Authorizer ¶
type Authorizer interface {
Authorize(ctx context.Context, req ClaimRequest) error
}
Authorizer checks a claim inline before delivery.
type ClaimGateway ¶
type ClaimGateway interface {
// Claim transfers ownership of a node-local warm sandbox to the caller and
// returns connection info. It performs the authorization check inline; it
// does NOT block on writing the SandboxClaim status.
Claim(ctx context.Context, req ClaimRequest) (Assignment, error)
// Release returns a sandbox to the node-local pool, or tears it down when
// the pool is over target. Never destroys a VM on pod-level state alone —
// see the vk-cocoon delete-authorization contract.
Release(ctx context.Context, assignment Assignment) error
}
ClaimGateway is the L2 node-local fast path for warm-pool claims. A claim is served by the node that already holds a warm microVM (via sandboxd), which hands over an already-running guest in sub-millisecond time and returns connection info immediately; the SandboxClaim object is reconciled to Bound asynchronously afterward. Authorization stays central through SubjectAccessReview.
type ClaimRecorder ¶
type ClaimRecorder interface {
RecordBound(ctx context.Context, claimNS, claimName string, a Assignment) error
}
ClaimRecorder durably records the "Bound" outcome of a delivered claim onto the SandboxClaim object. It is invoked asynchronously after Claim returns (kubelet static-Pod semantics: the node acts first, the apiserver records after) and again by the OrphanReconciler when an async record was lost.
func NewClaimRecorder ¶
func NewClaimRecorder(c client.Client) ClaimRecorder
NewClaimRecorder returns the default ClaimRecorder backed by a Kubernetes client (a cache-fed client in production, the fake client in tests).
type ClaimRequest ¶
type ClaimRequest struct {
Namespace string
ClaimName string
WarmPool string
// Selector optionally narrows which warm sandboxes are eligible (template
// blueprint hash, node affinity, etc.).
Selector map[string]string
}
ClaimRequest identifies a node-local warm-pool claim.
type ClientInventorySource ¶
type ClientInventorySource struct {
// contains filtered or unexported fields
}
ClientInventorySource is the production InventorySource: it reads NodeInventory objects through a controller-runtime reader. Back it with a cache-fed reader (cmd/sandbox-apiserver builds one scoped to exactly this GVK) so the O(nodes) enumeration is served from an informer, never a hot-path LIST off etcd. Objects are read as unstructured so any reader works without scheme wiring.
func NewClientInventorySource ¶
func NewClientInventorySource(reader client.Reader) *ClientInventorySource
NewClientInventorySource builds a ClientInventorySource over reader (use a cache-fed client in production).
func (*ClientInventorySource) ListNodes ¶
func (s *ClientInventorySource) ListNodes(ctx context.Context) ([]string, error)
ListNodes lists NodeInventory objects (O(nodes)) and returns their names.
func (*ClientInventorySource) NodeInventory ¶
func (s *ClientInventorySource) NodeInventory(ctx context.Context, node string) (*NodeInventory, error)
NodeInventory fetches and decodes one node's NodeInventory object.
type Delivery ¶
type Delivery struct {
SandboxName string
Node string
Address string
ClaimNS string
ClaimName string
}
Delivery is one live sandbox a node currently holds, with the claim it was delivered to. This is the node's own live state (the L0 node-scoped cache / sandboxd inventory), not a cluster-wide LIST.
type GatewayConfig ¶
type GatewayConfig struct {
// Node is the name of the node this gateway fronts (stamped into Assignments).
Node string
// Client delivers and releases sandboxes on this node.
Client SandboxdClient
// Authorizer checks claims inline.
Authorizer Authorizer
// Recorder durably records Bound asynchronously after delivery.
Recorder ClaimRecorder
// DefaultTemplate/DefaultNet/DefaultSize/TTLSeconds fill a ClaimSpec when the
// request Selector does not override them. An empty DefaultTemplate falls back
// to the WarmPool name as the template axis.
DefaultTemplate string
DefaultNet string
DefaultSize string
TTLSeconds int
// RecordTimeout bounds each async Bound-record attempt. Defaults to 10s.
RecordTimeout time.Duration
// BaseContext is the gateway's lifetime context; async record jobs derive from
// it rather than the (already-returned) request context. Defaults to
// context.Background().
BaseContext context.Context
// Logger records async-record failures (which the OrphanReconciler later
// heals). The zero logr.Logger discards.
Logger logr.Logger
}
GatewayConfig configures NewGateway.
type InventoryApplier ¶
type InventoryApplier interface {
Apply(ctx context.Context, inv *NodeInventory) error
}
InventoryApplier server-side-applies a NodeInventory object. The default implementation resolves the resource through a RESTMapper (never a naive kind+"s"); tests inject a fake.
func NewSSAInventoryApplier ¶
func NewSSAInventoryApplier(c client.Client, fieldOwner string) InventoryApplier
NewSSAInventoryApplier returns the default server-side-apply InventoryApplier.
type InventoryEntry ¶
type InventoryEntry = extv1beta1.InventoryEntry
InventoryEntry is one live sandbox as summarized by its owning node.
type InventorySource ¶
type InventorySource interface {
// ListNodes returns the nodes that publish inventory. O(nodes), cache-fed.
ListNodes(ctx context.Context) ([]string, error)
// NodeInventory returns one node's authoritative inventory. A partitioned or
// not-yet-published node returns an error, which List logs and skips.
NodeInventory(ctx context.Context, node string) (*NodeInventory, error)
}
InventorySource enumerates the per-node NodeInventory objects that back the aggregated store. It is deliberately granular — a node enumeration plus a per-node fetch — rather than one cluster-wide read, so:
- List fans out per node with bounded concurrency and a single partitioned node degrades to eventual consistency (its sandboxes are briefly absent) instead of failing the whole list, and
- Get can route to the single owning node (the README "route Get to the owning node, not the summary" contract).
In production ListNodes/NodeInventory are served from a cache-fed client listing NodeInventory objects at ResourceVersion=0 (O(nodes), never a hot-path LIST off etcd); tests inject StaticInventorySource.
type ListOptions ¶
ListOptions is the subset of client list parameters the aggregated store honors when fanning out to node inventories.
type NodeInventory ¶
type NodeInventory = extv1beta1.NodeInventory
NodeInventory is the single O(nodes) etcd object per node: the durable summary of that node's live sandboxes, server-side-applied on a slow cadence. The per-sandbox truth lives in the node (the L0 node-scoped cache), not etcd; a lost NodeInventory is rebuilt from the node's own live state on next publish. The canonical type (and its CRD) lives in the extensions.agents.x-k8s.io group; these aliases keep the scale contracts self-contained for callers.
type NodeInventoryPublisher ¶
type NodeInventoryPublisher struct {
// contains filtered or unexported fields
}
NodeInventoryPublisher server-side-applies one NodeInventory object for its node on a slow cadence, summarizing the node's live sandboxes. This is the entire L3 write path: O(nodes) applies, no per-sandbox etcd object.
func NewNodeInventoryPublisher ¶
func NewNodeInventoryPublisher(node string, live NodeLiveSource, applier InventoryApplier, log logr.Logger) *NodeInventoryPublisher
NewNodeInventoryPublisher builds a publisher for node, reading live state from live and applying via applier.
type NodeInventorySource ¶
NodeInventorySource enumerates the deliveries a node currently holds. A crashed gateway loses its in-memory holdings, but the node/sandboxd still hold the VMs, so this source (backed by sandboxd's own inventory) survives a gateway restart — which is exactly what lets the OrphanReconciler heal a lost Bound record.
type NodeLiveSource ¶
type NodeLiveSource interface {
LiveSandboxes(ctx context.Context) ([]InventoryEntry, error)
}
NodeLiveSource is a node's own live sandbox state — the sandboxd inventory / L0 node-scoped cache — NOT a cluster-wide LIST. A lost NodeInventory object is rebuilt from this on the next publish.
type OrphanReconciler ¶
type OrphanReconciler struct {
// contains filtered or unexported fields
}
OrphanReconciler heals orphan bindings: deliveries that happened but whose asynchronous Bound record was lost (the gateway crashed after delivery, before recording). It is audit-and-adopt only — it records the missing Bound and NEVER destroys a VM. Per the delete-authorization contract, a live VM is only ever torn down by owner-authorized Release, never as a side effect of GC.
func NewOrphanReconciler ¶
func NewOrphanReconciler(node string, inv NodeInventorySource, reader client.Client, recorder ClaimRecorder, logger logr.Logger) *OrphanReconciler
NewOrphanReconciler builds an OrphanReconciler. reader reads SandboxClaim Bound state with point Gets (no cluster-wide LIST); recorder adopts orphan bindings.
func (*OrphanReconciler) Reconcile ¶
func (o *OrphanReconciler) Reconcile(ctx context.Context) (int, error)
Reconcile scans the node's live deliveries, finds those whose owning SandboxClaim has no Bound record, and adopts them (records Bound). It returns the number of orphan bindings reconciled. It never releases or destroys a sandbox.
type PoolCapacity ¶
type PoolCapacity = extv1beta1.PoolCapacity
PoolCapacity is one node's warm capacity for a single pool. It aliases the canonical extensions type so the scale contracts stay self-contained.
type PoolKey ¶
PoolKey identifies a warm pool by the claim axes the aggregated Create path derives from a Sandbox: the template (blueprint image), the network mode, and the size class. A node advertises matching warm capacity as a NodeInventory PoolCapacity, and Create picks the node with the most warm capacity for the key.
func PoolKeyFor ¶
PoolKeyFor derives the warm-pool key for a workload. It is the SINGLE source of pool-key truth: the aggregated apiserver derives it from a Sandbox's podTemplate on Create, and the SandboxWarmPool driver derives it from a SandboxTemplate's podTemplate when setting node warm targets. Both MUST agree or a Create would never match the warm capacity the pool driver provisions (perpetual 503). Template = the first container image; Net = the given net (defaulted to NetDefault); Size = the first container's t-shirt class.
type ReviewAuthorizer ¶
type ReviewAuthorizer struct {
Reviewer SubjectAccessReviewer
// Group/Resource/Verb default to the SandboxClaim create check when empty.
Group string
Resource string
Verb string
}
ReviewAuthorizer checks SandboxClaim access through SubjectAccessReview.
func (*ReviewAuthorizer) Authorize ¶
func (a *ReviewAuthorizer) Authorize(ctx context.Context, req ClaimRequest) error
Authorize denies missing identities and runs SubjectAccessReview.
type SandboxLifecycle ¶
type SandboxLifecycle interface {
// Pause hibernates the sandbox: its state is snapshotted and the VM stops,
// freeing the node's memory. Idempotent on an already-paused sandbox.
Pause(ctx context.Context, node, id string) error
// Resume restores a paused sandbox and leaves it running. Idempotent on a
// running one.
Resume(ctx context.Context, node, id string) error
// Fork branches the sandbox into count children, each a fresh claim with
// its own id and lease. The parent is checkpointed in place and keeps
// running.
Fork(ctx context.Context, node, id string, count int, ttlSeconds int) ([]Assignment, error)
// Snapshot captures the sandbox's state as a named checkpoint that later
// claims can branch from. The source keeps running.
Snapshot(ctx context.Context, node, id, name string) (Snapshot, error)
// Snapshots lists the checkpoints on a node, newest first.
Snapshots(ctx context.Context, node string) ([]Snapshot, error)
// DeleteSnapshot removes a checkpoint. A missing checkpoint is success.
DeleteSnapshot(ctx context.Context, node, snapshotID string) error
// ClaimSnapshot delivers a fresh sandbox branched from a checkpoint.
ClaimSnapshot(ctx context.Context, node, snapshotID string, ttlSeconds int) (Assignment, error)
// Promote publishes the sandbox as a node-local template that later claims
// for that key clone from.
Promote(ctx context.Context, node, id, template string) (PoolKey, error)
// Stats reports one sandbox's resource usage.
Stats(ctx context.Context, node, id string) (SandboxStats, error)
}
SandboxLifecycle is the verb set a claimed sandbox supports after delivery. It is separate from SandboxStore's placement verbs because these all address an existing sandbox on a known node — there is no pool selection involved, only routing to the owner.
Latency is not uniform across these verbs and callers should not assume it is: Resume takes cocoon's mmap restore fast path (~55 ms) and Fork's children clone at 28–75 ms each, but Pause and Snapshot write the guest's memory out and therefore cost time proportional to its size.
type SandboxStats ¶
type SandboxStats struct {
CPUCount int
MemTotalBytes int64
MemUsedBytes int64
MemUsedMeasured bool
Paused bool
MeasuredAt time.Time
}
SandboxStats is one sandbox's resource usage. CPUCount and MemTotalBytes are the tier the VM was booted with and are authoritative; MemUsedBytes is only meaningful when MemUsedMeasured is true (a paused sandbox has no process to measure), so callers must not read zero as "idle".
type SandboxStore ¶
type SandboxStore interface {
// List assembles a SandboxList by fanning out to node inventories.
List(ctx context.Context, opts ListOptions) (*sandboxv1beta1.SandboxList, error)
// Get routes to the owning node for an authoritative (read-after-write)
// answer rather than the eventually-consistent summary.
Get(ctx context.Context, namespace, name string) (*sandboxv1beta1.Sandbox, error)
// Watch merges per-node inventory streams into a single sandbox watch.
Watch(ctx context.Context, opts ListOptions) (watch.Interface, error)
// Claim delivers a warm microVM for namespace/name from a node advertising warm
// capacity for pool, returning the node-local assignment (claim id, node,
// connection address). ttlSeconds sets the claim's lease; 0 means the node
// default. It writes NO per-sandbox etcd object: the claim is a synchronous
// node-local ownership transfer via the owning node's sandboxd.
// When no node has a warm microVM for the pool it returns an error for which
// IsNoWarmCapacity is true, so the caller can surface a retryable 503.
Claim(ctx context.Context, namespace, name string, pool PoolKey, ttlSeconds int) (Assignment, error)
// Release returns the claimed microVM id to its owning node's pool. It is
// owner-authorized teardown only (the Sandbox resource itself being deleted);
// it never destroys a VM on pod state alone. The node's sandboxd address is
// resolved from its NodeInventory.
Release(ctx context.Context, node, id string) error
// SandboxLifecycle is the post-claim verb set. Every verb routes to the
// owning node and stores nothing: the control plane stays O(pools+nodes)
// however many sandboxes are paused, forked, or checkpointed.
SandboxLifecycle
}
SandboxStore is the L3 storage contract behind an aggregated apiserver serving sandboxes.agents.x-k8s.io. It holds NO per-sandbox etcd objects: List/Get/Watch scatter-gather live node inventories and Create/Delete are synchronous node-local claim/release, so etcd stores only intent (warm-pool desired replicas plus one O(nodes) NodeInventory per node). This is the metrics.k8s.io pattern applied to sandboxes, so object count drops from O(sandboxes) to O(pools+nodes) while kubectl/RBAC/watch keep working.
type SandboxdClient ¶
type SandboxdClient interface {
Claim(ctx context.Context, spec sandboxd.ClaimSpec) (sandboxd.ClaimResult, error)
Release(ctx context.Context, id, token string) error
// The lifecycle verbs address an already-delivered sandbox by id. They all
// take sandboxd's operator path, authorized by the fleet api_token the
// client already carries, so the control plane needs no per-sandbox secret.
Hibernate(ctx context.Context, id string) error
Wake(ctx context.Context, id string) error
Fork(ctx context.Context, id string, spec sandboxd.ForkSpec) (sandboxd.ForkResult, error)
Checkpoint(ctx context.Context, id string, spec sandboxd.CheckpointSpec) (sandboxd.Checkpoint, error)
Checkpoints(ctx context.Context) ([]sandboxd.Checkpoint, error)
DeleteCheckpoint(ctx context.Context, checkpointID string) error
ClaimCheckpoint(ctx context.Context, checkpointID string, spec sandboxd.CheckpointClaimSpec) (sandboxd.ClaimResult, error)
Promote(ctx context.Context, id string, spec sandboxd.PromoteSpec) (sandboxd.PoolKey, error)
Stats(ctx context.Context, id string) (sandboxd.SandboxStats, error)
}
SandboxdClient is the subset of the sandboxd HTTP client the gateway needs, kept as an interface so tests inject a fake without a live node. *sandboxd.Client satisfies it.
type SandboxdClientFactory ¶
type SandboxdClientFactory func(addr, token string) SandboxdClient
SandboxdClientFactory builds a sandboxd client for one node's advertise address and the uniform fleet api_token. It is injected so tests need no live node and production wires the real HTTP client (NewSandboxdClientFactory).
func NewSandboxdClientFactory ¶
func NewSandboxdClientFactory() SandboxdClientFactory
NewSandboxdClientFactory returns the production SandboxdClientFactory: an HTTP sandboxd client per node advertise address (a bare "host:port" is given the http scheme; an address that already carries a scheme is used verbatim).
type Snapshot ¶
type Snapshot struct {
ID string
Name string
SandboxID string
Pool PoolKey
CreatedAt time.Time
// Node is the node holding the checkpoint. Checkpoints are node-local, so
// a caller needs it to branch from or delete this snapshot later.
Node string
}
Snapshot is a captured sandbox state that new sandboxes can branch from.
type StaticInventorySource ¶
type StaticInventorySource struct {
// contains filtered or unexported fields
}
StaticInventorySource is an in-memory InventorySource that doubles as an InventoryApplier: publishers Apply into it (the O(nodes) "etcd" writes) and the store reads from it (the cache-fed NodeInventory reads). Production swaps in a client-backed source listing NodeInventory objects at ResourceVersion=0.
func NewStaticInventorySource ¶
func NewStaticInventorySource() *StaticInventorySource
NewStaticInventorySource returns an empty source.
func (*StaticInventorySource) Apply ¶
func (s *StaticInventorySource) Apply(_ context.Context, inv *NodeInventory) error
Apply implements InventoryApplier: it stores the inventory and counts the apply, modeling the single O(nodes) server-side-apply write per node.
func (*StaticInventorySource) ApplyCount ¶
func (s *StaticInventorySource) ApplyCount() int
ApplyCount is the number of Apply calls, i.e. the size of the write path.
func (*StaticInventorySource) ListNodes ¶
func (s *StaticInventorySource) ListNodes(_ context.Context) ([]string, error)
ListNodes returns the known node names in stable order.
func (*StaticInventorySource) NodeInventory ¶
func (s *StaticInventorySource) NodeInventory(_ context.Context, node string) (*NodeInventory, error)
NodeInventory returns a copy of one node's inventory, or an error if the node is partitioned or unknown.
func (*StaticInventorySource) ObjectCount ¶
func (s *StaticInventorySource) ObjectCount() int
ObjectCount is the number of durable NodeInventory objects held — the O(nodes) etcd object count backing every synthesized sandbox.
func (*StaticInventorySource) Partition ¶
func (s *StaticInventorySource) Partition(node string)
Partition keeps node in ListNodes but makes NodeInventory(node) fail, modeling a node partitioned from the aggregated server.
func (*StaticInventorySource) Put ¶
func (s *StaticInventorySource) Put(inv *NodeInventory)
Put stores a node inventory directly (test seeding).
func (*StaticInventorySource) Remove ¶
func (s *StaticInventorySource) Remove(node string)
Remove drops a node's inventory object entirely (lost inventory).
type StoreOption ¶
type StoreOption func(*scatterGatherStore)
StoreOption configures a scatterGatherStore.
func WithClaimRouting ¶
func WithClaimRouting(token string, factory SandboxdClientFactory) StoreOption
WithClaimRouting enables the Create/Delete write path: token is the uniform fleet-wide sandboxd api_token presented on claim/release, and factory builds a per-node sandboxd client for a node's advertise address. Without it, Claim and Release fail closed and the store stays read-only.
func WithLogger ¶
func WithLogger(log logr.Logger) StoreOption
WithLogger sets the store logger. The zero logr.Logger discards.
func WithWatchPollInterval ¶
func WithWatchPollInterval(d time.Duration) StoreOption
WithWatchPollInterval sets how often Watch re-derives node inventory to emit deltas. Defaults to one second.
type SubjectAccessReviewer ¶
type SubjectAccessReviewer interface {
Create(ctx context.Context, sar *authzv1.SubjectAccessReview, opts metav1.CreateOptions) (*authzv1.SubjectAccessReview, error)
}
SubjectAccessReviewer creates a SubjectAccessReview and returns the decided object — the shape of client-go's authorizationv1client.SubjectAccessReviewInterface.Create, narrowed to an interface so the gateway needs no direct authz-client dependency and tests can inject a fake.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package apiserver assembles the aggregated apiserver that serves sandboxes.agents.x-k8s.io by scatter-gathering node inventory, the metrics.k8s.io pattern: no per-sandbox object is stored in etcd.
|
Package apiserver assembles the aggregated apiserver that serves sandboxes.agents.x-k8s.io by scatter-gathering node inventory, the metrics.k8s.io pattern: no per-sandbox object is stored in etcd. |
|
Package sandboxd is a small HTTP client for the node-local sandboxd warm-pool daemon (the sandbox repo's docs/sandboxd-api.md).
|
Package sandboxd is a small HTTP client for the node-local sandboxd warm-pool daemon (the sandbox repo's docs/sandboxd-api.md). |
|
Package warmpool drives the official agents.x-k8s.io SandboxWarmPool CRD onto the L3 node-local warm pools.
|
Package warmpool drives the official agents.x-k8s.io SandboxWarmPool CRD onto the L3 node-local warm pools. |