scale

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2026 License: AGPL-3.0 Imports: 32 Imported by: 0

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

View Source
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"
)
View Source
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"
)
View Source
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

View Source
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.

View Source
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

func AddressIPs(addr string) []string

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

func IsFallback(err error) bool

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

func IsNoWarmCapacity(err error) bool

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

func SizeClassForContainers(containers []corev1.Container) string

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

type ListOptions struct {
	Namespace     string
	LabelSelector string
	FieldSelector string
}

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.

func (*NodeInventoryPublisher) Publish

func (p *NodeInventoryPublisher) Publish(ctx context.Context) (int, error)

Publish reads the node's live sandboxes and server-side-applies a single NodeInventory object for the node, returning the number of summarized entries.

type NodeInventorySource

type NodeInventorySource interface {
	LiveDeliveries(ctx context.Context) ([]Delivery, error)
}

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

type PoolKey struct {
	Template string
	Net      string
	Size     string
}

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

func PoolKeyFor(containers []corev1.Container, net string) PoolKey

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

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.

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.

Jump to

Keyboard shortcuts

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