operator

package
v0.14.1 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package operator contains the narrow Kubernetes control-plane client used by the Rhiza recovery operator.

Index

Constants

This section is empty.

Variables

View Source
var ErrFencePending = errors.New("fencing: operation pending")

ErrFencePending is returned when the fencing service accepts the request (HTTP 202) and the proof is not yet ready.

View Source
var ErrMembershipUnauthorized = errors.New("membership management authentication failed")

Functions

func StoreIdentity

func StoreIdentity(env map[string]string) map[string]string

StoreIdentity contains only location fields, never credentials. Workload identity may differ, but it must address the same archive namespace.

Types

type APIError

type APIError struct {
	StatusCode int
}

APIError identifies a non-success Kubernetes response without copying the response body, which can contain sensitive resource data.

func (*APIError) Error

func (e *APIError) Error() string

type AutoDecision added in v0.14.0

type AutoDecision string

AutoDecision is a string enum for automatic recovery decisions.

const (
	AutoHealthy  AutoDecision = "Healthy"
	AutoDegraded AutoDecision = "Degraded"
	AutoSuspect  AutoDecision = "Suspect"
	AutoBlocked  AutoDecision = "Blocked"
	AutoRecover  AutoDecision = "Recover"
)

func DecideAutoRecovery added in v0.14.0

func DecideAutoRecovery(
	policy AutoPolicy,
	obs AutoObservation,
	now time.Time,
	suspectedSince time.Time,
	completed []time.Time,
) (AutoDecision, string)

DecideAutoRecovery is a pure function that decides whether automatic recovery should proceed given policy, observation, and time context. completed lists UTC timestamps of recoveries completed within the sliding 24-hour window. suspectedSince is the persisted timestamp of when the node first became suspect (zero value means unknown).

type AutoObservation added in v0.14.0

type AutoObservation struct {
	Known          bool
	Voters         int
	Reachable      int
	QuorumVerified bool
	StoreAvailable bool
	Durability     string
}

AutoObservation describes the current observable cluster state.

type AutoPolicy added in v0.14.0

type AutoPolicy struct {
	FailureGraceSeconds int64 `json:"failureGraceSeconds"`
	CooldownSeconds     int64 `json:"cooldownSeconds"`
	MaxRecoveriesPerDay int   `json:"maxRecoveriesPerDay"`
	AllowDataLoss       bool  `json:"allowDataLoss"`
}

AutoPolicy configures automatic recovery thresholds.

type ClusterResource added in v0.14.0

type ClusterResource struct {
	APIVersion string         `json:"apiVersion"`
	Kind       string         `json:"kind"`
	Metadata   map[string]any `json:"metadata"`
	Spec       ClusterSpec    `json:"spec"`
	Status     ClusterStatus  `json:"status,omitempty"`
}

ClusterResource opts an existing workload into unattended recovery. The fencing backend is a trusted authority, configured by the operator administrator rather than an arbitrary URL supplied by this resource.

type ClusterSpec added in v0.14.0

type ClusterSpec struct {
	LogicalID      string     `json:"logicalID"`
	StatefulSet    string     `json:"statefulSet"`
	Container      string     `json:"container"`
	AdoptClusterID string     `json:"adoptClusterID"`
	VoterPods      []VoterPod `json:"voterPods"`
	Automatic      bool       `json:"automatic"`
	Policy         AutoPolicy `json:"policy"`
}

type ClusterStatus added in v0.14.0

type ClusterStatus struct {
	Phase              string `json:"phase"`
	Message            string `json:"message"`
	ActiveClusterID    string `json:"activeClusterID,omitempty"`
	ActiveRecovery     string `json:"activeRecovery,omitempty"`
	ObservedGeneration int64  `json:"observedGeneration"`
}

type Controller

type Controller struct {
	Fencer        Fencer
	Automatic     bool
	Kube          *Kubernetes
	Bucket        objstore.Bucket
	Prefix        string
	HTTP          *http.Client
	StoreIdentity map[string]string
}

func (*Controller) Run

func (c *Controller) Run(ctx context.Context, interval time.Duration) error

type Fence

type Fence struct {
	RecoveryID     string `json:"recoveryID"`
	ClusterID      string `json:"clusterID"`
	StatefulSetUID string `json:"statefulSetUID"`
	Confirmed      bool   `json:"confirmed"`
	Evidence       string `json:"evidence"`
}

type FenceProof added in v0.14.0

type FenceProof struct {
	OperationID         string `json:"operationID"`
	SourceClusterID     string `json:"sourceClusterID"`
	BindingUID          string `json:"bindingUID"`
	RequestHash         string `json:"requestHash"`
	ProofID             string `json:"proofID"`
	ProcessesTerminated bool   `json:"processesTerminated"`
	RecreationBlocked   bool   `json:"recreationBlocked"`
	StorageQuiesced     bool   `json:"storageQuiesced"`
}

type FenceRequest added in v0.14.0

type FenceRequest struct {
	LogicalID       string        `json:"logicalID"`
	BindingUID      string        `json:"bindingUID"`
	Namespace       string        `json:"namespace"`
	StatefulSet     string        `json:"statefulSet"`
	StatefulSetUID  string        `json:"statefulSetUID"`
	SourceClusterID string        `json:"sourceClusterID"`
	OperationID     string        `json:"operationID"`
	Scope           Scope         `json:"scope"`
	Targets         []FenceTarget `json:"targets"`
}

type FenceTarget added in v0.14.0

type FenceTarget struct {
	NodeID      string `json:"nodeID"`
	Pod         string `json:"pod"`
	PodUID      string `json:"podUID"`
	WALIdentity string `json:"walIdentity"`
}

FenceTarget records an observed incarnation. Backends must fence the requested voter or generation authority, including recreation, not just this Pod UID.

type Fencer added in v0.14.0

type Fencer interface {
	Fence(context.Context, FenceRequest) (*FenceProof, error)
}

Fencer executes an immutable operation owned by the Operator. A pending or uncertain isolation must return an error, never a successful partial proof.

type FencingClient added in v0.14.0

type FencingClient struct {
	URL       string
	TokenFile string
	Client    *http.Client
}

FencingClient is the HTTPS backend; it does not own recovery decisions.

func (*FencingClient) Fence added in v0.14.0

func (fc *FencingClient) Fence(ctx context.Context, req FenceRequest) (*FenceProof, error)

type Kubernetes

type Kubernetes struct {
	BaseURL   string
	Namespace string
	TokenFile string
	Client    *http.Client
}

Kubernetes is the minimal in-cluster Kubernetes JSON REST client used by the operator. Callers construct rooted API paths and retain resourceVersion in values passed to Put.

func NewInCluster

func NewInCluster(namespace string) (*Kubernetes, error)

NewInCluster creates a TLS-verifying client from the mounted service account.

func (*Kubernetes) Get

func (k *Kubernetes) Get(ctx context.Context, apiPath string, out any) error

func (*Kubernetes) Post

func (k *Kubernetes) Post(ctx context.Context, apiPath string, value any, out any) error

func (*Kubernetes) Put

func (k *Kubernetes) Put(ctx context.Context, apiPath string, value any, out any) error

type KubernetesFencer added in v0.14.0

type KubernetesFencer struct{ Kube *Kubernetes }

KubernetesFencer submits immutable requests to a namespaced execution backend. Only the executor may write status; absent or incomplete evidence remains pending. The executor must establish real runtime, admission and storage barriers. Pod deletion or Kubernetes object disappearance alone is never sufficient evidence.

func (*KubernetesFencer) Fence added in v0.14.0

type MembershipAddJournal added in v0.14.0

type MembershipAddJournal struct {
	OperationID       string         `json:"operationID"`
	ExpectedConfigID  uint           `json:"expectedConfigID"`
	ExpectedAbortSlot quepaxa.Slot   `json:"expectedAbortSlot"`
	MemberID          quepaxa.NodeID `json:"memberID"`
	WALIdentity       string         `json:"walIdentity"`
	RequestHash       string         `json:"requestHash"`
	SecretUID         string         `json:"secretUID"`
	SecretResourceVer string         `json:"secretResourceVersion"`
}

MembershipAddJournal intentionally excludes the learner token. It binds the exact request bytes to one immutable credential Secret instead.

type MembershipFence added in v0.14.0

type MembershipFence struct {
	NodeID      quepaxa.NodeID `json:"nodeID"`
	WALIdentity string         `json:"walIdentity"`
	WorkloadUID string         `json:"workloadUID"`
	Confirmed   bool           `json:"confirmed"`
	Evidence    string         `json:"evidence"`
}

type MembershipSpec added in v0.14.0

type MembershipSpec struct {
	OperationID string          `json:"operationID"`
	Remove      quepaxa.NodeID  `json:"remove"`
	Fence       MembershipFence `json:"fence"`
	// VoterPods names every currently reachable voter that may form quorum.
	// It deliberately does not discover Pods or infer an ID from a Pod name.
	VoterPods         []VoterPod `json:"voterPods"`
	RemovedPod        string     `json:"removedPod,omitempty"`
	AbortAddition     bool       `json:"abortAddition,omitempty"`
	ReplacementPod    string     `json:"replacementPod"`
	ReplacementSecret string     `json:"replacementSecret"`
}

MembershipSpec replaces one externally fenced voter through a separately provisioned learner. The controller never creates, deletes, or rolls either workload.

type MembershipStatus added in v0.14.0

type MembershipStatus struct {
	Phase         string                    `json:"phase"`
	RemoveRequest *network.MembershipChange `json:"removeRequest,omitempty"`
	Add           *MembershipAddJournal     `json:"add,omitempty"`
}

MembershipStatus is the CR's durable side-effect journal. The remove request is retained directly; the add request is bound by hash to an immutable Secret without persisting its credential.

type Resource

type Resource struct {
	APIVersion string         `json:"apiVersion"`
	Kind       string         `json:"kind"`
	Metadata   map[string]any `json:"metadata"`
	Spec       Spec           `json:"spec"`
	Status     Status         `json:"status,omitempty"`
}

type Scope added in v0.14.0

type Scope string

Scope is "Generation" or "Voter".

const (
	ScopeGeneration Scope = "Generation"
	ScopeVoter      Scope = "Voter"
)

type Spec

type Spec struct {
	StatefulSet     string `json:"statefulSet"`
	Container       string `json:"container,omitempty"`
	SourceClusterID string `json:"sourceClusterID"`
	// Durability is the NEW generation's mode. The immutable source record,
	// not this desired setting, determines whether recovery can lose ACKs.
	Durability           string          `json:"durability"`
	RecoveryID           string          `json:"recoveryID,omitempty"`
	AllowDataLoss        bool            `json:"allowDataLoss,omitempty"`
	MaxArchiveAgeSeconds int64           `json:"maxArchiveAgeSeconds,omitempty"`
	Fence                Fence           `json:"fence,omitempty"`
	Membership           *MembershipSpec `json:"membership,omitempty"`
}

type Status

type Status struct {
	Phase              string                        `json:"phase"`
	Message            string                        `json:"message"`
	ObservedGeneration int64                         `json:"observedGeneration"`
	Source             string                        `json:"source"`
	Target             string                        `json:"target,omitempty"`
	RecoveryID         string                        `json:"recoveryID,omitempty"`
	SpecHash           string                        `json:"specHash,omitempty"`
	StatefulSetUID     string                        `json:"statefulSetUID,omitempty"`
	SourceDurability   string                        `json:"sourceDurability,omitempty"`
	SourceMembership   string                        `json:"sourceMembership,omitempty"`
	ManifestHash       string                        `json:"manifestHash,omitempty"`
	RecoveredTip       uint64                        `json:"recoveredTip,omitempty"`
	SecretName         string                        `json:"secretName,omitempty"`
	Stage              string                        `json:"stage,omitempty"`
	ArchiveCapture     string                        `json:"archiveCapture,omitempty"`
	Peers              []network.VoterRecoveryStatus `json:"peers,omitempty"`
	Membership         *MembershipStatus             `json:"membership,omitempty"`
}

type VoterPod added in v0.14.0

type VoterPod struct {
	NodeID quepaxa.NodeID `json:"nodeID"`
	Pod    string         `json:"pod"`
}

Jump to

Keyboard shortcuts

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