Skip to main content

SnapshotPhase

Enum SnapshotPhase 

Source
pub enum SnapshotPhase {
    Pending,
    Running,
    Succeeded,
    Failed,
    Deleting,
    Discovered,
    Unchanged,
    Unknown(String),
}
Expand description

Lifecycle phase of a Snapshot.

use kopiur_api::SnapshotPhase;
use kopiur_api::common::PhaseLabel;

// Canonical values round-trip as bare strings.
assert_eq!(serde_json::to_value(SnapshotPhase::Succeeded).unwrap(), "Succeeded");
let p: SnapshotPhase = serde_json::from_value(serde_json::json!("Running")).unwrap();
assert_eq!(p, SnapshotPhase::Running);

// A phase written by a NEWER operator decodes into `Unknown` (never a
// watcher-poisoning serde error) and re-serializes verbatim.
let p: SnapshotPhase = serde_json::from_value(serde_json::json!("Quiescing")).unwrap();
assert_eq!(p, SnapshotPhase::Unknown("Quiescing".into()));
assert_eq!(serde_json::to_value(&p).unwrap(), "Quiescing");
assert_eq!(p.label(), "Quiescing");
// Never terminal: an unrecognized phase is held and surfaced, not finished.
assert!(!p.is_terminal());

Variants§

§

Pending

Admitted, not yet started (also the default).

§

Running

Mover Job is in flight.

§

Succeeded

Snapshot created successfully.

§

Failed

Mover Job exhausted its retries.

§

Deleting

CR is being deleted; finalizer is reclaiming the snapshot.

§

Discovered

Catalog-materialized backup kopiur didn’t produce.

§

Unchanged

The backup ran to completion but kopia wrote no new manifest: the source was byte-identical to the previous snapshot, and this policy has files.ignoreIdenticalSnapshots enabled.

Terminal, and a success: the source was read and hashed, and it is protected — by the previous snapshot, which remains the live restore point. So an Unchanged run advances every liveness signal (last-backup timestamp, policy health, failure-streak reset) exactly like Succeeded.

What it does NOT do is own a kopia manifest. status.snapshot is absent, the finalizer has nothing to reclaim, and it takes no GFS retention slot — a restore point that does not exist must not displace one that does. Recording it as Succeeded instead would make the controller resolve “its” snapshot and find its predecessor’s, leaving two CRs claiming one manifest and the first prune deleting it out from under the second.

Unreachable unless the policy opts in: the mover pins --ignore-identical-snapshots=false at the identity scope on every run. See #351.

§

Unknown(String)

A phase string this build does not recognize — written by a newer operator during a rolling upgrade, or persisted before this variant set existed. Decode-compat only: hidden from the CRD schema (the apiserver rejects it on every new write) and never produced by this build.

Never terminal, never schedulable, never reapable, never a success — every consumer holds and surfaces it rather than acting on a phase whose meaning it does not know.

Implementations§

Source§

impl SnapshotPhase

Source

pub fn is_terminal(&self) -> bool

Whether this phase is terminal: the operator will do no further work on the object of its own accord, so a diagnostic must not report it as in-flight (nor as stuck).

Deleting is deliberately not terminal. A CR sitting in Deleting has a finalizer that is still trying to reclaim its kopia snapshot — a wedged finalizer (an unreachable backend, a held mass-deletion breaker) is in-flight work that never completes, which is exactly the state worth surfacing. Classifying it terminal is how a stuck deletion becomes invisible.

Discovered and Unchanged ARE terminal: a discovered CR mirrors a kopia snapshot the operator did not produce and never advances on its own, and an Unchanged run already finished (successfully, owning no manifest). A Discovered CR that is later deleted moves to Deleting like any other, so nothing is lost by treating the phase itself as done.

Pure + exhaustive so the single definition lives in one tested place — the CLI and the controller must never disagree about what “still working” means.

use kopiur_api::SnapshotPhase;

assert!(SnapshotPhase::Succeeded.is_terminal());
assert!(SnapshotPhase::Failed.is_terminal());
assert!(SnapshotPhase::Discovered.is_terminal());
assert!(SnapshotPhase::Unchanged.is_terminal());
assert!(!SnapshotPhase::Pending.is_terminal());
assert!(!SnapshotPhase::Running.is_terminal());
// A wedged finalizer is in-flight work, not a finished object.
assert!(!SnapshotPhase::Deleting.is_terminal());
// An unrecognized phase is never terminal — hold and surface it.
assert!(!SnapshotPhase::Unknown("Quiescing".into()).is_terminal());
Source

pub fn is_unknown(&self) -> bool

Whether this phase is the decode sentinel — a value the running build cannot interpret, kept verbatim by Unknown instead of failing the whole typed list()/watch (#359, defect 3).

The contract is narrow on purpose, and it is the reason this is a method rather than an inline matches! at each caller: true means only “this string is not a phase this binary knows”, never “unusual” or “not one I handle”. A canonical variant added to this enum later is by definition not the sentinel, so false is the right answer for it — which is exactly why the exhaustive match below is written out. Callers asking a set-shaped question (“is this finished?”, “is this a failure?”) want is_terminal or their own exhaustive match, not this.

use kopiur_api::SnapshotPhase;

assert!(SnapshotPhase::Unknown("Quiescing".into()).is_unknown());
assert!(!SnapshotPhase::Succeeded.is_unknown());
assert!(!SnapshotPhase::Failed.is_unknown());
assert!(!SnapshotPhase::Deleting.is_unknown());

Trait Implementations§

Source§

impl Clone for SnapshotPhase

Source§

fn clone(&self) -> SnapshotPhase

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SnapshotPhase

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SnapshotPhase

Source§

fn default() -> SnapshotPhase

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for SnapshotPhase

Source§

fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for SnapshotPhase

Source§

impl JsonSchema for SnapshotPhase

Source§

fn schema_name() -> Cow<'static, str>

The name of the generated JSON Schema. Read more
Source§

fn json_schema(_: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
§

fn inline_schema() -> bool

Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being re-used where possible using the $ref keyword. Read more
§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

impl PartialEq for SnapshotPhase

Source§

fn eq(&self, other: &SnapshotPhase) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl PhaseLabel for SnapshotPhase

Source§

const ALL: &'static [Self]

Every canonical variant, in declaration order. Never contains Unknown.
Source§

fn label(&self) -> &str

The stable metric/wire label string for this variant (exhaustive match); the Unknown arm echoes the stored value verbatim so a read-modify-write never mutates a phase this build does not understand.
Source§

fn unknown(raw: String) -> Self

Build the decode-compat fallback for a non-canonical stored string. Implemented by the phase_serde! macro’s host enum.
Source§

fn parse(s: &str) -> Option<Self>

Parse a canonical label; None for anything else (including a value that would decode to Unknown). Derived from ALL + label() so a new variant is parseable the moment it is declared.
Source§

fn canonical() -> Vec<&'static str>

The canonical label set, for the CRD schema enum and “valid values” messages. One definition, derived from ALL.
Source§

impl Serialize for SnapshotPhase

Source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for SnapshotPhase

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> AnyExt for T
where T: Any + ?Sized,

§

fn downcast_ref<T>(this: &Self) -> Option<&T>
where T: Any,

Attempts to downcast this to T behind reference
§

fn downcast_mut<T>(this: &mut Self) -> Option<&mut T>
where T: Any,

Attempts to downcast this to T behind mutable reference
§

fn downcast_rc<T>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>>
where T: Any,

Attempts to downcast this to T behind Rc pointer
§

fn downcast_arc<T>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>>
where T: Any,

Attempts to downcast this to T behind Arc pointer
§

fn downcast_box<T>(this: Box<Self>) -> Result<Box<T>, Box<Self>>
where T: Any,

Attempts to downcast this to T behind Box pointer
§

fn downcast_move<T>(this: Self) -> Option<T>
where T: Any, Self: Sized,

Attempts to downcast owned Self to T, useful only in generic context as a workaround for specialization
§

impl<T> AsDebug for T
where T: Debug,

§

fn as_debug(&self) -> &dyn Debug

Returns self as a &dyn Debug trait object.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T, X> CoerceTo<T> for X
where T: CoerceFrom<X> + ?Sized,

§

fn coerce_rc_to(self: Rc<X>) -> Rc<T>

§

fn coerce_box_to(self: Box<X>) -> Box<T>

§

fn coerce_ref_to(&self) -> &T

§

fn coerce_mut_to(&mut self) -> &mut T

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> ServiceExt for T

§

fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>
where Self: Sized,

Apply a transformation to the response body. Read more
§

fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>
where Self: Sized,

High level tracing that classifies responses using HTTP status codes. Read more
§

fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>
where Self: Sized,

High level tracing that classifies responses using gRPC headers. Read more
§

fn follow_redirects(self) -> FollowRedirect<Self>
where Self: Sized,

Follow redirect resposes using the Standard policy. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> TryIntoValue for T
where T: Serialize,

§

type Error = SerializationError

§

fn try_into_value(self) -> Result<Value, <T as TryIntoValue>::Error>

§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more