Skip to content

feat: Add Repository::lock_repo #163

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion crates/core/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
pub(crate) mod hotcold;
pub(crate) mod ignore;
pub(crate) mod local_destination;
pub(crate) mod lock;
pub(crate) mod node;
pub(crate) mod stdin;
pub(crate) mod warm_up;

use std::{io::Read, ops::Deref, path::PathBuf, sync::Arc};

use bytes::Bytes;
use chrono::{DateTime, Local};
use enum_map::Enum;
use log::trace;
use log::{debug, trace};

#[cfg(test)]
use mockall::mock;
Expand Down Expand Up @@ -337,6 +339,35 @@
///
/// The result of the removal.
fn remove(&self, tpe: FileType, id: &Id, cacheable: bool) -> RusticResult<()>;

/// Specify if the backend is able to lock files
fn can_lock(&self) -> bool {
false

Check warning on line 345 in crates/core/src/backend.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend.rs#L344-L345

Added lines #L344 - L345 were not covered by tests
}

/// Lock the given file.
///
/// # Arguments
///
/// * `tpe` - The type of the file.
/// * `id` - The id of the file.
/// * `until` - The date until when to lock. May be `None` which usually specifies a unlimited lock
///
/// # Errors
///
/// If the file could not be read.
fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {
debug!("no locking implemented. {tpe:?}, {id}, {until:?}");

Check warning on line 360 in crates/core/src/backend.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend.rs#L359-L360

Added lines #L359 - L360 were not covered by tests

if self.can_lock() {

Check warning on line 362 in crates/core/src/backend.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend.rs#L362

Added line #L362 was not covered by tests
unimplemented!("Using default implementation. No locking implemented in backend.");
} else {
Err(RusticError::new(
ErrorKind::Backend,
"No locking configured on backend.",

Check warning on line 367 in crates/core/src/backend.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend.rs#L365-L367

Added lines #L365 - L367 were not covered by tests
))
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -374,6 +405,12 @@
fn remove(&self, tpe: FileType, id: &Id, cacheable: bool) -> RusticResult<()> {
self.deref().remove(tpe, id, cacheable)
}
fn can_lock(&self) -> bool {
self.deref().can_lock()

Check warning on line 409 in crates/core/src/backend.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend.rs#L409

Added line #L409 was not covered by tests
}
fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {
self.deref().lock(tpe, id, until)

Check warning on line 412 in crates/core/src/backend.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend.rs#L412

Added line #L412 was not covered by tests
}
}

impl ReadBackend for Arc<dyn WriteBackend> {
Expand Down
16 changes: 16 additions & 0 deletions crates/core/src/backend/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
};

use bytes::Bytes;
use chrono::{DateTime, Local};
use dirs::cache_dir;
use log::{trace, warn};
use walkdir::WalkDir;
Expand Down Expand Up @@ -226,6 +227,21 @@
}
self.be.remove(tpe, id, cacheable)
}

fn can_lock(&self) -> bool {
self.be.can_lock()
}

fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {
if !self.can_lock() {
return Err(RusticError::new(

Check warning on line 237 in crates/core/src/backend/cache.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/cache.rs#L235-L237

Added lines #L235 - L237 were not covered by tests
ErrorKind::Backend,
"No locking configured on backend.",
));
}

self.be.lock(tpe, id, until)
}
}

/// Backend that caches data in a directory.
Expand Down
16 changes: 16 additions & 0 deletions crates/core/src/backend/decrypt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{num::NonZeroU32, sync::Arc};

use bytes::Bytes;
use chrono::{DateTime, Local};
use crossbeam_channel::{Receiver, unbounded};
use rayon::prelude::*;
use zstd::stream::{copy_encode, decode_all, encode_all};
Expand Down Expand Up @@ -636,6 +637,21 @@
fn remove(&self, tpe: FileType, id: &Id, cacheable: bool) -> RusticResult<()> {
self.be.remove(tpe, id, cacheable)
}

fn can_lock(&self) -> bool {
self.be.can_lock()

Check warning on line 642 in crates/core/src/backend/decrypt.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/decrypt.rs#L641-L642

Added lines #L641 - L642 were not covered by tests
}

fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {
if !self.can_lock() {
return Err(RusticError::new(
ErrorKind::Backend,
"No locking configured on backend.",

Check warning on line 649 in crates/core/src/backend/decrypt.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/decrypt.rs#L645-L649

Added lines #L645 - L649 were not covered by tests
));
}

self.be.lock(tpe, id, until)

Check warning on line 653 in crates/core/src/backend/decrypt.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/decrypt.rs#L653

Added line #L653 was not covered by tests
}
}

#[cfg(test)]
Expand Down
15 changes: 15 additions & 0 deletions crates/core/src/backend/dry_run.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bytes::Bytes;
use chrono::{DateTime, Local};
use zstd::decode_all;

use crate::{
Expand Down Expand Up @@ -164,4 +165,18 @@
self.be.remove(tpe, id, cacheable)
}
}

fn can_lock(&self) -> bool {
self.be.can_lock()

Check warning on line 170 in crates/core/src/backend/dry_run.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/dry_run.rs#L169-L170

Added lines #L169 - L170 were not covered by tests
}

fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {
if !self.can_lock() {
return Err(RusticError::new(
ErrorKind::Backend,
"No locking configured on backend.",

Check warning on line 177 in crates/core/src/backend/dry_run.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/dry_run.rs#L173-L177

Added lines #L173 - L177 were not covered by tests
));
}
self.be.lock(tpe, id, until)

Check warning on line 180 in crates/core/src/backend/dry_run.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/dry_run.rs#L180

Added line #L180 was not covered by tests
}
}
16 changes: 16 additions & 0 deletions crates/core/src/backend/hotcold.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::sync::Arc;

use bytes::Bytes;
use chrono::{DateTime, Local};

use crate::{
ErrorKind, RusticError,
backend::{FileType, ReadBackend, WriteBackend},
error::RusticResult,
id::Id,
Expand Down Expand Up @@ -98,4 +100,18 @@
}
Ok(())
}

fn can_lock(&self) -> bool {
self.be.can_lock()
}

fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {
if !self.can_lock() {
return Err(RusticError::new(

Check warning on line 110 in crates/core/src/backend/hotcold.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/hotcold.rs#L108-L110

Added lines #L108 - L110 were not covered by tests
ErrorKind::Backend,
"No locking configured on backend.",
));
}
self.be.lock(tpe, id, until)
}
}
142 changes: 142 additions & 0 deletions crates/core/src/backend/lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::{process::Command, sync::Arc};

use bytes::Bytes;
use chrono::{DateTime, Local};
use log::debug;

use crate::{
CommandInput, ErrorKind, RusticError, RusticResult,
backend::{FileType, ReadBackend, WriteBackend},
id::Id,
};

/// A backend which warms up files by simply accessing them.
#[derive(Clone, Debug)]
pub struct LockBackend {
/// The backend to use.
be: Arc<dyn WriteBackend>,
/// The command to be called to lock files in the backend
command: CommandInput,
}

impl LockBackend {
/// Creates a new `WarmUpAccessBackend`.
///
/// # Arguments
///
/// * `be` - The backend to use.
pub fn new_lock(be: Arc<dyn WriteBackend>, command: CommandInput) -> Arc<dyn WriteBackend> {

Check warning on line 28 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L28

Added line #L28 was not covered by tests
Arc::new(Self { be, command })
}
}

impl ReadBackend for LockBackend {
fn location(&self) -> String {

Check warning on line 34 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L34

Added line #L34 was not covered by tests
self.be.location()
}

fn list_with_size(&self, tpe: FileType) -> RusticResult<Vec<(Id, u32)>> {

Check warning on line 38 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L38

Added line #L38 was not covered by tests
self.be.list_with_size(tpe)
}

fn read_full(&self, tpe: FileType, id: &Id) -> RusticResult<Bytes> {

Check warning on line 42 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L42

Added line #L42 was not covered by tests
self.be.read_full(tpe, id)
}

fn read_partial(

Check warning on line 46 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L46

Added line #L46 was not covered by tests
&self,
tpe: FileType,
id: &Id,
cacheable: bool,
offset: u32,
length: u32,
) -> RusticResult<Bytes> {
self.be.read_partial(tpe, id, cacheable, offset, length)
}

fn list(&self, tpe: FileType) -> RusticResult<Vec<Id>> {

Check warning on line 57 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L57

Added line #L57 was not covered by tests
self.be.list(tpe)
}

fn needs_warm_up(&self) -> bool {
self.be.needs_warm_up()
}

fn warm_up(&self, tpe: FileType, id: &Id) -> RusticResult<()> {
self.be.warm_up(tpe, id)
}
}

fn path_to_id_from_file_type(tpe: FileType, id: &Id) -> String {

Check warning on line 70 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L70

Added line #L70 was not covered by tests
let hex_id = id.to_hex();
match tpe {
FileType::Config => "config".into(),
FileType::Pack => format!("data/{}/{}", &hex_id[0..2], &*hex_id),
_ => format!("{}/{}", tpe.dirname(), &*hex_id),

Check warning on line 75 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L72-L75

Added lines #L72 - L75 were not covered by tests
}
}

impl WriteBackend for LockBackend {
fn create(&self) -> RusticResult<()> {
self.be.create()
}

fn write_bytes(&self, tpe: FileType, id: &Id, cacheable: bool, buf: Bytes) -> RusticResult<()> {

Check warning on line 84 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L84

Added line #L84 was not covered by tests
self.be.write_bytes(tpe, id, cacheable, buf)
}

fn remove(&self, tpe: FileType, id: &Id, cacheable: bool) -> RusticResult<()> {
self.be.remove(tpe, id, cacheable)
}

fn can_lock(&self) -> bool {
true
}

fn lock(&self, tpe: FileType, id: &Id, until: Option<DateTime<Local>>) -> RusticResult<()> {

Check warning on line 96 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L96

Added line #L96 was not covered by tests
if !self.can_lock() {
return Err(RusticError::new(
ErrorKind::Backend,
"No locking configured on backend.",
));
}

let until = until.map_or_else(String::new, |u| u.to_rfc3339());

Check warning on line 104 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L104

Added line #L104 was not covered by tests

let path = path_to_id_from_file_type(tpe, id);

Check warning on line 106 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L106

Added line #L106 was not covered by tests

let args = self.command.args().iter().map(|c| {
c.replace("%id", &id.to_hex())
.replace("%type", tpe.dirname())
.replace("%path", &path)
.replace("%until", &until)

Check warning on line 112 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L108-L112

Added lines #L108 - L112 were not covered by tests
});

debug!("calling {:?}...", self.command);

Check warning on line 115 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L115

Added line #L115 was not covered by tests

let status = Command::new(self.command.command())

Check warning on line 117 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L117

Added line #L117 was not covered by tests
.args(args)
.status()
.map_err(|err| {
RusticError::with_source(

Check warning on line 121 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L120-L121

Added lines #L120 - L121 were not covered by tests
ErrorKind::Internal,
"error calling lock command for {tpe}, id: {id}.",
err,
)
.attach_context("tpe", tpe.to_string())
.attach_context("id", id.to_string())

Check warning on line 127 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L126-L127

Added lines #L126 - L127 were not covered by tests
})?;

if !status.success() {
return Err(RusticError::new(

Check warning on line 131 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L130-L131

Added lines #L130 - L131 were not covered by tests
ErrorKind::Backend,
"lock command was not successful for {tpe}, id: {id}. {status}",
)
.attach_context("tpe", tpe.to_string())
.attach_context("id", id.to_string())
.attach_context("status", status.to_string()));

Check warning on line 137 in crates/core/src/backend/lock.rs

View check run for this annotation

Codecov / codecov/patch

crates/core/src/backend/lock.rs#L135-L137

Added lines #L135 - L137 were not covered by tests
}

Ok(())
}
}
1 change: 1 addition & 0 deletions crates/core/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod dump;
pub mod forget;
pub mod init;
pub mod key;
pub mod lock;
pub mod merge;
pub mod prune;
/// The `repair` command.
Expand Down
Loading
Loading