Skip to content

Commit

Permalink
VERY WIP gcp
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Dec 23, 2023
1 parent 0cd1f8a commit 215535f
Show file tree
Hide file tree
Showing 11 changed files with 1,555 additions and 52 deletions.
819 changes: 769 additions & 50 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]

resolver = "2"

members = [
"taskchampion/taskchampion",
"taskchampion/sync-server",
Expand All @@ -25,6 +27,7 @@ env_logger = "^0.10.0"
ffizz-header = "0.5"
flate2 = "1"
futures = "^0.3.25"
google-cloud-storage = "0.15.0"
lazy_static = "1"
libc = "0.2.136"
log = "^0.4.17"
Expand All @@ -38,6 +41,7 @@ serde = { version = "^1.0.147", features = ["derive"] }
strum = "0.25"
strum_macros = "0.25"
tempfile = "3"
tokio = { version = "1", features = ["rt-multi-thread"] }
thiserror = "1.0"
ureq = "^2.9.0"
uuid = { version = "^1.6.0", features = ["serde", "v4"] }
11 changes: 9 additions & 2 deletions taskchampion/taskchampion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ documentation = "https://docs.rs/crate/taskchampion"
repository = "https://github.com/GothenburgBitFactory/taskwarrior"
readme = "../README.md"
license = "MIT"
edition = "2018"
edition = "2021"
resolver = "2"

[features]
default = ["server-sync" ]
default = ["server-sync", "server-gcp"] # temporary
server-sync = ["crypto", "dep:ureq"]
server-gcp = ["cloud", "dep:google-cloud-storage", "dep:tokio"]
crypto = ["dep:ring"]
cloud = []

[package.metadata.docs.rs]
all-features = true
Expand All @@ -33,7 +36,11 @@ strum_macros.workspace = true
flate2.workspace = true
byteorder.workspace = true
ring.workspace = true
google-cloud-storage.workspace = true
tokio.workspace = true

google-cloud-storage.optional = true
tokio.optional = true
ureq.optional = true
ring.optional = true

Expand Down
2 changes: 2 additions & 0 deletions taskchampion/taskchampion/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ other_error!(io::Error);
other_error!(serde_json::Error);
other_error!(rusqlite::Error);
other_error!(crate::storage::sqlite::SqliteError);
#[cfg(feature = "server-gcp")]
other_error!(google_cloud_storage::http::Error);

pub type Result<T> = std::result::Result<T, Error>;
1 change: 1 addition & 0 deletions taskchampion/taskchampion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Support for some optional functionality is controlled by feature flags.
Sync server client support:
* `server-gcp` - sync to Google Cloud Platform
* `server-sync` - sync to the taskchampion-sync-server
# See Also
Expand Down
74 changes: 74 additions & 0 deletions taskchampion/taskchampion/src/server/cloud/gcp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![allow(unused_variables, dead_code)]
use super::service::Service;
use google_cloud_storage::client::{Client, ClientConfig};
use google_cloud_storage::http::objects;
use tokio::runtime::Runtime;

/// A [`Service`] implementation based on the Google Cloud Storage service.
pub(in crate::server) struct GcpService {
client: Client,
rt: Runtime,
bucket: String,
encryption_secret: Vec<u8>,
}

impl GcpService {
pub(in crate::server) fn new(bucket: String, encryption_secret: Vec<u8>) -> Self {
let config = ClientConfig::default().anonymous(); // TODO
Self {
client: Client::new(config),
rt: Runtime::new().expect("Could not build async runtime"),
bucket,
encryption_secret,
}
}
}

impl Service for GcpService {
fn put(&mut self, name: &[u8], value: &[u8]) -> crate::errors::Result<()> {
let name = String::from_utf8(name.to_vec()).expect("non-UTF8 object name");
let upload_type = objects::upload::UploadType::Simple(objects::upload::Media::new(name));
self.rt.block_on(self.client.upload_object(
&objects::upload::UploadObjectRequest {
bucket: self.bucket.clone(),
..Default::default()
},
value.to_vec(),
&upload_type,
))?;
Ok(())
}

fn get(&mut self, name: &[u8]) -> crate::errors::Result<Option<Vec<u8>>> {
let name = String::from_utf8(name.to_vec()).expect("non-UTF8 object name");
let data = self.rt.block_on(self.client.download_object(
&objects::get::GetObjectRequest {
bucket: self.bucket.clone(),
object: name,
..Default::default()
},
&objects::download::Range::default(),
))?;
Ok(Some(data))
}

fn del(&mut self, name: &[u8]) -> crate::errors::Result<()> {
todo!()
}

fn list<'a>(
&'a mut self,
prefix: &'a [u8],
) -> crate::errors::Result<Box<dyn Iterator<Item = &'a [u8]> + 'a>> {
todo!()
}

fn compare_and_swap(
&mut self,
name: &[u8],
existing_value: Option<Vec<u8>>,
new_value: Vec<u8>,
) -> crate::errors::Result<bool> {
todo!()
}
}
16 changes: 16 additions & 0 deletions taskchampion/taskchampion/src/server/cloud/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*!
* Support for cloud-service-backed sync.
*
* All of these operate using a similar approach, with specific patterns of object names. The
* process of adding a new version requires a compare-and-swap operation that sets a new version
* as the "latest" only if the existing "latest" has the expected value. This ensures a continuous
* chain of versions, even if multiple replicas attempt to sync at the same time.
*/

mod server;
mod service;

pub(in crate::server) use server::CloudServer;

#[cfg(feature = "server-gcp")]
pub(in crate::server) mod gcp;
Loading

0 comments on commit 215535f

Please sign in to comment.