Help us shape the future of Go! Take the 2026 Go Developer Survey

aggapi

package
v0.33.11 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package aggapi is a thin client for the state-snapshotter aggregated subresource API exposed through the kube-apiserver aggregation layer.

After C9 there is no whole-subtree server-side aggregation: each snapshot node exposes three per-CR subresources, addressed by the node's resource plural:

  • manifests-download: the node's own captured manifests (status preserved, namespace made relative). Served by the node's OWN subresource group (core group for the core Snapshot, the domain-prefixed group for domain snapshot CRs — the domain apiserver proxies to the core content layer); CSI VolumeSnapshot leaves use the dedicated storage-foundation VS-connector group instead (distinct from the CSI VolumeSnapshot CRD's own group, which neither storage-foundation nor state-snapshotter owns).
  • manifests-with-data-restoration: a ready-to-apply manifest array for the node's whole subtree (the server delegates domain subtrees internally). Served by the node's OWN subresource group (core group for the core Snapshot, the domain-prefixed group for domain snapshot CRs).
  • manifests-and-children-refs-upload: import one node's manifests plus its direct child refs. Served by the node's OWN subresource group (core group for the core Snapshot, the domain-prefixed group for domain snapshot CRs — the domain apiserver's upload facade records the node's own childrenSnapshotRefs and forwards the manifests to the core content layer); CSI VolumeSnapshot leaves use the dedicated storage-foundation VS-connector group instead. The upload is bind-first: it returns 409 ImportContentNotBound until the node's SnapshotContent is bound.

All three subresources now route the same way — by the node's own group — so there is no longer any download/upload group asymmetry. Every subresource is addressed by the node's own namespaced CR (Snapshot, domain snapshot CR, or CSI VolumeSnapshot leaf). The client never reads cluster-scoped SnapshotContent objects.

Index

Constants

View Source
const (
	// CoreSubresourcesGroup is the core controller's aggregated subresources API group.
	// It serves manifests-download for every node kind and the core Snapshot's
	// restore/upload subresources.
	CoreSubresourcesGroup = "subresources.state-snapshotter.deckhouse.io"
	// CoreSubresourcesVersion is the version served under CoreSubresourcesGroup.
	CoreSubresourcesVersion = "v1alpha1"

	// DomainSubresourcesGroupPrefix is prepended to a domain snapshot's API group to
	// address its aggregated subresources group (e.g. "sds-unified-snapshots-poc.deckhouse.io"
	// -> "subresources.sds-unified-snapshots-poc.deckhouse.io").
	DomainSubresourcesGroupPrefix = "subresources."

	// VSConnectorGroup is the generic-PVC extended VolumeSnapshot connector subresource
	// group, owned and served by storage-foundation. It is intentionally NOT the CSI
	// external-snapshotter group (VolumeSnapshotGroup below) — storage-foundation only
	// exposes subresources on the VolumeSnapshot's namespaced identity, it does not own
	// the VolumeSnapshot CRD itself.
	VSConnectorGroup = "subresources.storage-foundation.deckhouse.io"
	// VSConnectorVersion is the version served under VSConnectorGroup.
	VSConnectorVersion = "v1"

	// StorageGroup is the API group of the core Snapshot / SnapshotContent CRDs.
	StorageGroup = "state-snapshotter.deckhouse.io"
	// VolumeSnapshotGroup is the CSI external-snapshotter API group of VolumeSnapshot leaves.
	// This is the real CSI CRD group — neither storage-foundation nor state-snapshotter owns
	// it — and is a distinct group from VSConnectorGroup above; do not conflate the two.
	VolumeSnapshotGroup = "snapshot.storage.k8s.io"
	// VolumeSnapshotVersion is the API version of the CSI external-snapshotter
	// VolumeSnapshot CRD group (VolumeSnapshotGroup). It is independent of
	// VSConnectorVersion (the subresources group's version) even though both currently
	// equal "v1" — the two happen to match today, but do not reuse one for the other.
	VolumeSnapshotVersion = "v1"
	// VolumeSnapshotResource is the resource plural of CSI VolumeSnapshot objects.
	VolumeSnapshotResource = "volumesnapshots"
	// VolumeSnapshotKind is the kind of CSI VolumeSnapshot leaf nodes.
	VolumeSnapshotKind = "VolumeSnapshot"
)

Aggregated subresource API groups and versions.

View Source
const (
	// SubManifestsDownload reads one node's own captured manifests.
	SubManifestsDownload = "manifests-download"
	// SubManifestsRestore reads a ready-to-apply manifest array for a node's subtree.
	SubManifestsRestore = "manifests-with-data-restoration"
	// SubManifestsUpload imports one node's manifests plus its direct child refs.
	SubManifestsUpload = "manifests-and-children-refs-upload"
)

Subresource names.

View Source
const (
	// DefaultMaxResponseBytes caps one aggregated-API response at 64 MiB. Manifest
	// bundles may be large, but must not grow client memory without a finite bound.
	DefaultMaxResponseBytes int64 = 64 << 20
)
View Source
const ReasonImportContentNotBound = "ImportContentNotBound"

ReasonImportContentNotBound is the canonical status.reason of the bind-first 409 the namespaced (core / domain / VS-connector) manifests-and-children-refs-upload returns while a node's status.boundSnapshotContentName is still empty. It mirrors state-snapshotter usecase.ReasonImportContentNotBound verbatim — the wire contract d8 retries on until the binder binds the node's SnapshotContent.

Variables

View Source
var ErrResponseTooLarge = errors.New("aggregated API response exceeds byte limit")

ErrResponseTooLarge reports that an aggregated-API response exceeded its byte budget.

View Source
var ErrRestoreBadRequest = errors.New("restore request rejected as invalid")

ErrRestoreBadRequest is returned by RestoreManifestsScoped when the server rejects the request as invalid (state-snapshotter restore.ErrBadRequest wire equivalent: an unknown scope value, an object filter missing kind or name, or an object filter used with a scope other than RestoreScopeNode). Distinguish it from other failures with errors.Is.

View Source
var ErrRestoreNotFound = errors.New("restore target not found")

ErrRestoreNotFound is returned by RestoreManifestsScoped when the addressed node, or — with an object filter — the requested object within it, does not exist. Distinguish it from other failures with errors.Is.

Functions

This section is empty.

Types

type Client

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

Client performs aggregated-apiserver calls over an absolute-path REST interface (typically a discovery REST client) and resolves a node's GVK to its resource plural via a RESTMapper.

func NewClient

func NewClient(restClient rest.Interface, mapper meta.RESTMapper) *Client

NewClient builds a Client from a raw REST interface (e.g. the discovery REST client) and a RESTMapper used to resolve GVK -> resource plural.

func NewClientForConfig

func NewClientForConfig(cfg *rest.Config, mapper meta.RESTMapper) (*Client, error)

NewClientForConfig builds a Client from a rest.Config, constructing the discovery REST client internally. mapper resolves GVK -> resource plural.

func (*Client) LeafDataExportTarget

func (c *Client) LeafDataExportTarget(ref NodeRef) (string, string, string, error)

LeafDataExportTarget resolves the DataExport targetRef {group, resource, kind} for a snapshot leaf node. CSI VolumeSnapshot leaves use the fixed constants VolumeSnapshotGroup / VolumeSnapshotResource / VolumeSnapshotKind; all other kinds derive group from the leaf's apiVersion, use its own kind directly, and resolve the resource plural via the RESTMapper.

This is used to build a DataExport that the storage-volume-data-manager controller can route through its kind-agnostic snapshot export path (categorySnapshot): group/kind must identify a namespaced snapshot CR so the controller can read its status.boundSnapshotContentName → SnapshotContent → status.dataRef.

TEMP REVERTME: resource (plural) is populated in addition to kind so the DataExport satisfies the deployed storage-volume-data-manager (mr135) GVR-based CRD, which requires spec.targetRef.resource. The kind-based contract is not yet in SVDM main. Sending both resource and kind is safe because each CRD prunes the field it doesn't know.

func (*Client) NodeManifestsDownload

func (c *Client) NodeManifestsDownload(ctx context.Context, ref NodeRef) ([]byte, error)

NodeManifestsDownload performs GET <node>/manifests-download and returns the raw JSON array body (the node's own captured manifests).

The call is retried with bounded exponential backoff on a transient aggregated-API error (see isTransientManifestsDownloadError) — observed in practice as the aggregated APIService backend briefly restarting/overloading and returning "the server is currently unable to handle the request" (HTTP 503).

func (*Client) RestoreManifests

func (c *Client) RestoreManifests(ctx context.Context, ref NodeRef, targetNamespace string) ([]byte, error)

RestoreManifests performs GET <node>/manifests-with-data-restoration?targetNamespace=<ns> and returns the raw apply-ready JSON array body for the node's whole subtree.

It is a thin wrapper over RestoreManifestsScoped with a zero-value RestoreScopeOptions; callers that need scope=node or an object filter call RestoreManifestsScoped directly.

func (*Client) RestoreManifestsScoped

func (c *Client) RestoreManifestsScoped(ctx context.Context, ref NodeRef, targetNamespace string, opts RestoreScopeOptions) ([]byte, error)

RestoreManifestsScoped performs GET <node>/manifests-with-data-restoration with targetNamespace and, when set, the server's scope/kind/name/apiVersion query params (restore_handler.go parseRestoreQueryOptions). Each of opts.Scope/FilterKind/FilterName/FilterAPIVersion is sent ONLY when non-empty — an empty Scope omits the scope param entirely rather than sending scope=subtree explicitly, reproducing the server's own default byte-for-byte.

func (*Client) UploadManifests

func (c *Client) UploadManifests(ctx context.Context, ref NodeRef, body []byte) ([]byte, error)

UploadManifests performs POST <node>/manifests-and-children-refs-upload with the given JSON body ({"manifests": <array>, "childRefs": [...]}) and returns the raw body.

The upload is bind-first: the aggregated apiserver refuses it with 409 ImportContentNotBound until the addressed node's status.boundSnapshotContentName is set. The import orchestrator (see snapimport.Run) already gates the whole upload pass behind a collective wait-for-bind, so this per-request retry is only a safety net for the narrow read-after-write race where the CLI observed a node bound but this upload endpoint has not yet — see postManifestsUpload.

type NodeRef

type NodeRef struct {
	APIVersion string
	Kind       string
	Name       string
	Namespace  string
}

NodeRef uniquely identifies a snapshot node for aggregated subresource calls. It is intentionally string-based (no typed objects) so that domain-specific kinds can be addressed without registering their Go types.

func (NodeRef) IsVolumeSnapshotLeaf

func (r NodeRef) IsVolumeSnapshotLeaf() bool

IsVolumeSnapshotLeaf reports whether the ref addresses a CSI VolumeSnapshot leaf, which is served by the VS-connector subresource group instead of the core group.

type RestoreScope

type RestoreScope string

RestoreScope selects the compilation depth of a RestoreManifestsScoped call, mirroring state-snapshotter's usecase/restore.Scope wire values verbatim (restore_handler.go parseRestoreQueryOptions) — these literals go on the wire as the "scope" query param, so they must match the server exactly, not just read similarly.

const (
	// RestoreScopeSubtree compiles the addressed node and its whole subtree, recursively. It is
	// the server's default when the scope query param is omitted entirely.
	RestoreScopeSubtree RestoreScope = "subtree"
	// RestoreScopeNode compiles ONLY the addressed node, with no descendants. Required before an
	// object filter (FilterKind/FilterName/FilterAPIVersion) is accepted.
	RestoreScopeNode RestoreScope = "node"
)

type RestoreScopeOptions

type RestoreScopeOptions struct {
	Scope            RestoreScope
	FilterKind       string
	FilterName       string
	FilterAPIVersion string
}

RestoreScopeOptions narrows a RestoreManifestsScoped call to one node (Scope == RestoreScopeNode) and, optionally, a single captured object within that node (FilterKind + FilterName, with FilterAPIVersion further disambiguating). The server accepts the object filter ONLY together with Scope == RestoreScopeNode and rejects any other combination with a 400 (restore_handler.go parseRestoreQueryOptions) — RestoreManifestsScoped does not pre-validate this client-side, it lets the server enforce its own contract and surfaces the resulting ErrRestoreBadRequest. A zero-value RestoreScopeOptions reproduces today's default (full subtree, no filter) byte-for-byte: no scope/kind/name/apiVersion query params are sent at all.

Jump to

Keyboard shortcuts

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