Skip to main content

KopiaClient

Struct KopiaClient 

Source
pub struct KopiaClient { /* private fields */ }
Expand description

A kopia client backed by the real kopia binary via tokio::process.

Construction is pure — building a client never spawns a process. Only the async methods invoke kopia. The builder defaults the binary to kopia (resolved via PATH); inject a path for tests or non-standard images:

use std::path::PathBuf;
use kopiur_kopia::KopiaClient;

let client = KopiaClient::builder().build();
assert_eq!(client.binary(), &PathBuf::from("kopia"));

let custom = KopiaClient::builder()
    .binary("/usr/local/bin/kopia")
    .env("KOPIA_PASSWORD", "s3cr3t")
    .build();
assert_eq!(custom.binary(), &PathBuf::from("/usr/local/bin/kopia"));

Implementations§

Source§

impl KopiaClient

Source

pub fn builder() -> KopiaClientBuilder

Start building a client.

Source

pub fn binary(&self) -> &PathBuf

The configured binary path (useful for diagnostics / tests).

Source

pub fn common_env(&self) -> &BTreeMap<String, String>

The environment applied to every invocation (useful for tests asserting that the cache/log/config dirs were injected).

Source

pub fn common_env_remove(&self) -> &BTreeSet<String>

The environment-variable names UNSET on every invocation (useful for tests asserting a credential is structurally kept away from a client).

Source

pub fn common_args(&self) -> &[String]

The global args appended after the subcommand on every invocation (useful for tests asserting e.g. --no-auto-maintenance is always present).

Source

pub fn default_timeout(&self) -> Option<Duration>

The timeout applied to every invocation when set (useful for tests asserting a caller time-bounds its subprocesses).

Source

pub async fn repository_connect( &self, spec: &ConnectSpec, cache: CacheTuning, ) -> Result<(), KopiaError>

Connect to an existing repository (kopia repository connect <backend>). cache sizes this connection’s local kopia cache; pass CacheTuning::default to leave kopia’s defaults.

Source

pub async fn repository_connect_readonly( &self, spec: &ConnectSpec, cache: CacheTuning, ) -> Result<(), KopiaError>

Connect to an existing repository read-only (kopia repository connect <backend> --readonly). The read-only bit persists in kopia’s client config, so every subsequent invocation on this connection is structurally unable to mutate the repository — the connect mode for browse sessions. Every other mover flow stays on the read-write Self::repository_connect.

Source

pub async fn repository_connect_with( &self, spec: &ConnectSpec, cache: CacheTuning, opts: ConnectOptions, ) -> Result<(), KopiaError>

Connect to an existing repository with explicit ConnectOptions (kopia repository connect <backend> [--readonly] [--persist-credentials]). Self::repository_connect and Self::repository_connect_readonly both delegate here with the options that reproduce their historical argv byte-for-byte. The replication mover’s source connect uses readonly + persist_credentials together so snapshot migrate --source-config can later read the source password from the persisted credentials.

Source

pub async fn repository_create( &self, spec: &ConnectSpec, cache: CacheTuning, create_opts: &CreateOptions, ) -> Result<(), KopiaError>

Create a new repository (kopia repository create <backend>). cache sizes the creating connection’s local cache; pass CacheTuning::default to leave kopia’s defaults. create_opts carries the create-time-fixed knobs (encryption/splitter/hash algorithms, ECC) baked into the repository format.

Source

pub async fn repository_throttle_set( &self, throttle: &ThrottleArgs, ) -> Result<(), KopiaError>

Set the repository’s throttling limits (kopia repository throttle set). Caps upload/download bytes-per-sec and read/list/upload ops-per-sec so a run doesn’t saturate a link or hammer an object store (ADR-0005 §13(e)). A no-op (skips the call) when nothing is set.

Source

pub async fn repository_set_client_read_only( &self, read_only: bool, ) -> Result<(), KopiaError>

Flip the CONNECTED repository’s client-side read-only bit (kopia repository set-client --read-only / --read-write), issue #374.

Three properties, all pinned against kopia 0.23.1 by crates/kopia/tests/integration_set_client_throttle.rs:

  • Works on a read-only connection. set-client registers kopia’s repositoryReaderAction, so it opens fine against a config connected with --readonly — unlike Self::repository_set_parameters, which needs a real blob write and hard-errors there.
  • The bit lives in the local config, not the repository. read_only: true writes "readonly": true into the KOPIA_CONFIG_PATH JSON; false removes the key. The test fingerprints every blob in the backend across the flip and requires it unchanged, so this never mutates shared state and needs no maintenance ownership/lease.
  • It is the “flip window” primitive for a read-only-connected config that must run a write-requiring verb: flip read-write, run the verb, flip back. Note that Self::repository_throttle_set does not need this window — M3a measured it succeeding directly on a --readonly connection, and leaving the read-only bit intact, because it only rewrites the local config’s throttlingLimits. The flip is defensive there, not required.
Source

pub async fn repository_set_parameters( &self, params: &SetParametersArgs, ) -> Result<(), KopiaError>

Rewrite mutable repository parameters on the CONNECTED repository (kopia repository set-parameters [flags]), issue #258. No-op when nothing is set.

Two properties the caller must respect:

  • Never on a read-only connection. kopia hard-errors (unable to write blobcfg blob: PutBlob() failed for "kopia.blobcfg": storage is read-only), so a mode: ReadOnly repository must not reach here.
  • This invalidates every other client’s cached format blob. kopia says so itself (“you must disconnect and re-connect all other Kopia clients”) and drops the local kopia.repository/kopia.blobcfg cache. Other clients re-read within formatBlobCacheDuration (15m) on their own, but this is why the caller applies only on observed drift rather than unconditionally.

Needs no maintenance ownership/lease.

Source

pub async fn repository_sync_to( &self, destination: &ConnectSpec, opts: &SyncToOptions, ) -> Result<(), KopiaError>

Mirror the connected repository’s blobs to a destination backend (kopia repository sync-to <destination> [flags]), ADR-0005 §13(d) / issue #216. The caller must already be connected to the source repository; this copies its blobs to destination. The destination’s backend args are built by ConnectSpec::backend_args (the same builder connect/create use), so a new backend variant is wired through automatically. opts carries the tuning knobs (parallelism, --delete, the must-exist/times/update tri-states, throughput caps) — see SyncToOptions. Destination credentials are supplied via the environment, never on argv, exactly like connect/create. Success is exit code 0.

Source

pub async fn repository_sync_to_with_env( &self, destination: &ConnectSpec, opts: &SyncToOptions, dest_env: &BTreeMap<String, Option<String>>, ) -> Result<(), KopiaError>

Self::repository_sync_to with a per-invocation environment overlay applied to the sync-to subprocess only. The replication mover uses this to give the destination backend its own credentials: it maps each of the destination backend’s env-delivered credential vars (AWS_*, AZURE_*, …) from the KOPIUR_DEST_-prefixed copy in its environment, and unsets any that the destination doesn’t provide so a source credential cannot leak. The source repository is read from the persisted connection config (kopia bakes the source storage credentials in at repository connect), so overlaying the plain credential names here cannot disturb the source read. Some(v) sets a var, None removes it. Credentials travel via env, never argv.

Source

pub async fn snapshot_create( &self, source_path: &str, tags: &BTreeMap<String, String>, override_source: Option<&str>, ) -> Result<SnapshotCreateResult, KopiaError>

Create a snapshot of source_path with the given tags (key:value) and kopia’s own defaults for snapshot create’s tuning knobs. Returns the parsed create result.

override_source, when set, is passed to kopia as --override-source (format username@hostname:path). This is how Kopiur records snapshots under the operator-resolved identity (ADR §4.2 / anchoring principle 9) rather than the mover pod’s ambient user@host. Without it kopia would attribute the snapshot to the pod, breaking the identity model that the whole catalog/retention/restore machinery keys on.

Source

pub async fn snapshot_create_with( &self, source_path: &str, tags: &BTreeMap<String, String>, override_source: Option<&str>, opts: &SnapshotCreateOptions, ) -> Result<SnapshotCreateResult, KopiaError>

Create a snapshot honoring the operator’s SnapshotCreateOptions (failFast, uploadLimitMb, description — M4 flag sweep, issue #216). Same identity/tags contract as Self::snapshot_create, which now delegates here with an all-default opts (byte-for-byte the same argv as before this option struct existed).

Source

pub async fn snapshot_create_outcome_with( &self, source_path: &str, tags: &BTreeMap<String, String>, override_source: Option<&str>, opts: &SnapshotCreateOptions, ) -> Result<SnapshotCreateOutcome, KopiaError>

Self::snapshot_create_with, but able to say “kopia deliberately wrote no manifest”.

snapshot create has exactly one legitimate silent success: with the retention knob ignoreIdenticalSnapshots on and the source byte-identical to the previous snapshot, kopia exits 0, prints nothing on stdout, and says so only on stderr. Through run_json that was a hard EmptyOutput error and terminally failed the Snapshot CR (#351).

Returning an enum rather than an Option/sentinel is deliberate: a deduped run owns no kopia manifest, and callers that quietly treated it as an ordinary success would go looking for one — which is how a CR ends up claiming its predecessor’s manifest and deleting it on prune. The variant forces every caller to decide.

Any other empty stdout still fails, loudly and now with kopia’s own stderr attached.

Source

pub async fn snapshot_list( &self, filter: Option<&SnapshotSource>, ) -> Result<Vec<SnapshotListEntry>, KopiaError>

List snapshots, optionally filtered by source identity. With no filter this lists all snapshots in the repository.

Source

pub async fn snapshot_list_all( &self, ) -> Result<Vec<SnapshotListEntry>, KopiaError>

List EVERY snapshot in the repository regardless of owning identity (kopia snapshot list --json --all).

What --all actually does (verified against kopia 0.23.1, whose help text — “Show all snapshots (not just current username/host)” — reads more broadly than the behavior): the flag only takes effect when a <source> positional is supplied. A source-less snapshot list --json already returns every identity in the repository, foreign ones included; it is NOT scoped to the connected user@host. So Self::snapshot_list with filter: None is not, today, the identity-scoped call the flag name suggests.

Use this one anyway wherever the count or the set is load-bearing — replication enumeration, post-migrate verification, the spec.seed empty-repository backstop. Those are decisions that strand or accept a repository, and none of them should rest on a kopia default that could change without the flag name changing with it. The behavior above is pinned by the sync_to_seeds_* integration test, which asserts a foreign-identity-only repository lists its snapshots BOTH ways.

Source

pub async fn snapshot_migrate( &self, opts: &SnapshotMigrateOptions, ) -> Result<(), KopiaError>

Copy snapshot manifests (and their content) from ANOTHER repository into the CONNECTED one (kopia snapshot migrate --source-config <path>). The caller must already be connected to the destination; the source repository is opened from the config file named in SnapshotMigrateOptions::source_config_path, authenticating with that config’s PERSISTED password (see ConnectOptions::persist_credentials) — this client’s KOPIA_PASSWORD belongs to the destination. snapshot migrate has no --json; success is exit code 0, like Self::repository_sync_to.

CAVEAT — kopia exits 0 even when a per-source migration failed: the per-source migration goroutines only LOG their errors (verified against kopia 0.23.1’s cli/command_snapshot_migrate.go), so a zero exit does NOT mean every selected snapshot arrived. Callers MUST post-verify by listing the destination (Self::snapshot_list_all) and checking that every expected (identity, startTime) pair is present.

Source

pub async fn snapshot_delete(&self, id: &str) -> Result<(), KopiaError>

Delete a single snapshot by manifest id. kopia’s snapshot delete requires --delete to actually remove (otherwise it dry-runs) and does not support --json; success is signaled by exit code 0.

IDEMPOTENT: an already-absent snapshot (no snapshots matched <id> on stderr) is success — that IS the goal state. kopia dedups identical-content snapshot manifests, so several Snapshot CRs can legitimately pin the SAME kopia id; when GFS retention prunes more than one of them, the first finalizer’s delete removes the manifest and the rest would otherwise fail terminally, wedging their CRs in Deleting forever (caught by the retention e2e under suite load).

Source

pub async fn snapshot_restore( &self, id: &str, target_dir: &str, ) -> Result<(), KopiaError>

Restore a snapshot’s contents to a target directory with kopia’s default options. kopia’s snapshot restore does not emit JSON; success is exit code 0.

Source

pub async fn snapshot_restore_with( &self, id: &str, target_dir: &str, opts: &RestoreOptions, ) -> Result<(), KopiaError>

Restore a snapshot honoring the operator’s RestoreOptions (enableFileDeletion, ignorePermissionErrors, writeFilesAtomically, …). Success is exit code 0.

Source

pub async fn snapshot_verify( &self, opts: &VerifyOptions, ) -> Result<(), KopiaError>

Verify repository/snapshot integrity (kopia snapshot verify). Success is exit code 0; a verification failure surfaces as a non-zero exit.

Source

pub async fn snapshot_estimate( &self, source_path: &str, ) -> Result<(), KopiaError>

Estimate the size/scope of snapshotting source_path (kopia snapshot estimate). Best-effort; success is exit code 0.

Source

pub async fn snapshot_pin(&self, id: &str, pin: &str) -> Result<(), KopiaError>

Add a pin to a snapshot so maintenance/expiration never deletes it (kopia snapshot pin <id> --add <pin>). Used to protect snapshots whose Snapshot carries deletionPolicy: Retain.

Source

pub async fn snapshot_unpin( &self, id: &str, pin: &str, ) -> Result<(), KopiaError>

Remove a pin from a snapshot (kopia snapshot pin <id> --remove <pin>).

Source

pub async fn snapshot_expire(&self, delete: bool) -> Result<(), KopiaError>

Expire snapshots per the repository’s policy (kopia snapshot expire --all). When delete is false this is a dry-run (kopia requires --delete to actually remove). Success is exit code 0.

Source

pub async fn repository_validate_provider(&self) -> Result<(), KopiaError>

Validate that the connected storage provider behaves correctly (kopia repository validate-provider). A good Repository-readiness preflight for object-store backends. Success is exit code 0.

Source

pub async fn policy_set( &self, target: &str, policy: &PolicyArgs, ) -> Result<(), KopiaError>

Apply a policy to target (an identity string, a path, or --global) via kopia policy set. The operator calls this before the first snapshot so SnapshotPolicy.spec.policy (compression/splitter/ignore) is honored.

Source

pub async fn policy_show(&self, target: &str) -> Result<Value, KopiaError>

Show the effective policy for target (kopia policy show <target> --json), parsed as a generic JSON value.

Source

pub async fn repository_status(&self) -> Result<RepositoryStatus, KopiaError>

Get repository status (kopia repository status --json).

This spawns kopia, so the example is no_run (it would need a real binary + connected repository):

use kopiur_kopia::KopiaClient;

let client = KopiaClient::builder()
    .env("KOPIA_PASSWORD", "s3cr3t")
    .build();
let status = client.repository_status().await?;
println!("repository unique id: {}", status.unique_id_hex);
Source

pub async fn maintenance_info(&self) -> Result<MaintenanceInfo, KopiaError>

Get maintenance info (kopia maintenance info --json).

Source

pub async fn index_blob_count(&self) -> Result<i64, KopiaError>

Count the repository’s content-index blobs (kopia index list --json, array length). kopia’s index is compacted by periodic maintenance; when maintenance stops (e.g. a stale lease owner), this grows unbounded and kopia eventually warns “Found too many index blobs (N), ensure periodic repository maintenance”. The operator surfaces a Kubernetes Warning when this crosses a configurable threshold. Cheap — lists index-blob metadata only, no content read. Verified against kopia 0.23 (index list --[no-]json).

Source

pub async fn maintenance_set_owner_me(&self) -> Result<(), KopiaError>

Claim the repository’s maintenance ownership for the currently connected identity (kopia maintenance set --owner me). kopia ties “who may run maintenance” to the connected user@hostname and rejects a maintenance run from anyone but the designated owner (“maintenance must be run by designated user: …”). A repo bootstrapped by the controller in-process is owned by the controller’s identity, so a mover Job (a different pod) MUST claim ownership before it can run maintenance. Idempotent; no JSON, success is exit 0.

Source

pub async fn maintenance_set_owner(&self, owner: &str) -> Result<(), KopiaError>

Set the repository’s maintenance owner to an EXPLICIT user@hostname (kopia maintenance set --owner <owner>). Used by the bootstrap mover right after repository create to stamp the stable, lease-derived owner (kopiur_api::maintenance::kopia_owner_for_lease) instead of the creating pod’s ephemeral identity — without this, every later maintenance mover sees a foreign owner and takeoverPolicy: Never yields forever. Verified against kopia 0.23 maintenance set --help (--owner=OWNER Set maintenance owner user@hostname).

Source

pub async fn repository_set_client_identity( &self, username: &str, hostname: &str, ) -> Result<(), KopiaError>

Switch the CONNECTED client identity (kopia repository set-client --username … --hostname …). The maintenance mover assumes the stable lease-derived identity this way (kopia 0.23 has no identity override on repository connect; the OS user@pod-hostname is ephemeral), so maintenance set --owner me records a stable string and the designated-user check passes on every later run. Verified against kopia 0.23 repository set-client --help.

Source

pub async fn maintenance_run( &self, mode: MaintenanceMode, ) -> Result<(), KopiaError>

Run a maintenance pass. kopia’s maintenance run does not emit JSON; success is exit code 0. The caller must already be the designated maintenance owner (see maintenance_set_owner_me).

Source

pub async fn run_raw_streaming( &self, args: &[String], sink: &mut (dyn AsyncWrite + Unpin + Send), ) -> Result<u64, KopiaError>

Run kopia with args and stream stdout byte-for-byte into sink (no line splitting, no UTF-8 assumption), returning the byte count on a zero exit. This is the file-content path for kopia show <file-oid> (the browse data-plane’s cat/download), where stdout is the raw object bytes — buffering it whole or splitting it into lines would corrupt/clamp arbitrarily large binary files.

stderr is accumulated like every other invocation; a non-zero exit yields KopiaError::NonZeroExit with the stderr tail. NOTE: bytes already streamed before a late failure have reached the sink — callers writing to a file should verify the count and discard partial output on error (kopia’s show either streams the object or fails up front, so in practice a failure produces no payload). Honors default_timeout.

Source

pub fn server_start( &self, spec: &ServerStartSpec, password: Option<&str>, ) -> KopiaError

Run kopia server start, replacing this process with kopia via exec.

On success this never returns (kopia takes over the PID and runs until it is signalled). It returns a KopiaError only if exec itself fails (e.g. the binary is missing). The repository must already be connected (call KopiaClient::repository_connect first) — the server reads the connected repo from the kopia config file.

password is the UI password for ServerAuthMode::Password; it is appended to argv here, inside the server pod, so it never reaches the controller, a ConfigMap, or the pure [server_start_args] builder. For ServerAuthMode::None it is ignored.

Trait Implementations§

Source§

impl Clone for KopiaClient

Source§

fn clone(&self) -> KopiaClient

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 KopiaClient

Source§

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

Formats the value using the given formatter. Read more

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