Skip to content

Commit 68d1c63

Browse files
committed
feat(storage): initial client and integration test
1 parent d8d1a9a commit 68d1c63

File tree

7 files changed

+425
-5
lines changed

7 files changed

+425
-5
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/integration-tests/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,14 @@ wkt.workspace = true
4141
package = "google-cloud-firestore"
4242
path = "../../src/firestore"
4343

44+
[dependencies.storage]
45+
package = "google-cloud-storage"
46+
path = "../../src/storage"
47+
4448
[dependencies.sm]
4549
package = "google-cloud-secretmanager-v1"
4650
path = "../../src/generated/cloud/secretmanager/v1"
51+
default-features = false
4752

4853
[dependencies.smo]
4954
package = "secretmanager-openapi-v1"

src/integration-tests/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub type Result<T> = std::result::Result<T, gax::error::Error>;
1717
pub mod error_details;
1818
pub mod firestore;
1919
pub mod secret_manager;
20+
pub mod storage;
2021
pub mod workflows;
2122

2223
pub const SECRET_ID_LENGTH: usize = 64;

src/integration-tests/src/storage.rs

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use crate::Error;
16+
use crate::Result;
17+
use gax::paginator::{ItemPaginator, Paginator};
18+
19+
pub const BUCKET_ID_LENGTH: usize = 32;
20+
21+
pub async fn buckets(builder: storage::client::ClientBuilder) -> Result<()> {
22+
// Enable a basic subscriber. Useful to troubleshoot problems and visually
23+
// verify tracing is doing something.
24+
#[cfg(feature = "log-integration-tests")]
25+
let _guard = {
26+
use tracing_subscriber::fmt::format::FmtSpan;
27+
let subscriber = tracing_subscriber::fmt()
28+
.with_level(true)
29+
.with_thread_ids(true)
30+
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
31+
.finish();
32+
33+
tracing::subscriber::set_default(subscriber)
34+
};
35+
36+
let project_id = crate::project_id()?;
37+
let client = builder.build().await?;
38+
39+
cleanup_stale_buckets(&client, &project_id).await?;
40+
41+
Ok(())
42+
}
43+
44+
async fn cleanup_stale_buckets(client: &storage::client::Storage, project_id: &str) -> Result<()> {
45+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
46+
let stale_deadline = SystemTime::now()
47+
.duration_since(UNIX_EPOCH)
48+
.map_err(Error::other)?;
49+
let stale_deadline = stale_deadline - Duration::from_secs(48 * 60 * 60);
50+
let stale_deadline = wkt::Timestamp::clamp(stale_deadline.as_secs() as i64, 0);
51+
52+
let mut items = client
53+
.list_buckets(format!("projects/{project_id}"))
54+
.paginator()
55+
.await
56+
.items();
57+
let mut pending = Vec::new();
58+
let mut names = Vec::new();
59+
while let Some(bucket) = items.next().await {
60+
let item = bucket?;
61+
if let Some("true") = item.labels.get("integration-test").map(String::as_str) {
62+
if let Some(true) = item.create_time.map(|v| v < stale_deadline) {
63+
let client = client.clone();
64+
let name = item.name.clone();
65+
pending.push(tokio::spawn(
66+
async move { cleanup_bucket(client, name).await },
67+
));
68+
names.push(item.name);
69+
}
70+
}
71+
}
72+
73+
let r: std::result::Result<Vec<_>, _> = futures::future::join_all(pending)
74+
.await
75+
.into_iter()
76+
.collect();
77+
r.map_err(Error::other)?
78+
.into_iter()
79+
.zip(names)
80+
.for_each(|(r, name)| println!("deleting bucket {name} resulted in {r:?}"));
81+
82+
Ok(())
83+
}
84+
85+
async fn cleanup_bucket(client: storage::client::Storage, name: String) -> Result<()> {
86+
let mut objects = client
87+
.list_objects(&name)
88+
.set_versions(true)
89+
.paginator()
90+
.await
91+
.items();
92+
let mut pending = Vec::new();
93+
while let Some(object) = objects.next().await {
94+
let object = object?;
95+
pending.push(
96+
client
97+
.delete_object(object.bucket, object.name)
98+
.set_generation(object.generation)
99+
.send(),
100+
);
101+
}
102+
let _ = futures::future::join_all(pending).await;
103+
client.delete_bucket(&name).send().await
104+
}

src/integration-tests/tests/driver.rs

+11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ mod driver {
7878
.map_err(report)
7979
}
8080

81+
#[test_case(storage::client::Storage::builder().with_tracing().with_retry_policy(retry_policy()); "with tracing enabled")]
82+
#[test_case(storage::client::Storage::builder().with_retry_policy(retry_policy()); "with retry enabled")]
83+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
84+
async fn run_storage_buckets(
85+
builder: storage::client::ClientBuilder,
86+
) -> integration_tests::Result<()> {
87+
integration_tests::storage::buckets(builder)
88+
.await
89+
.map_err(report)
90+
}
91+
8192
#[test_case(ta::client::TelcoAutomation::builder().with_tracing(); "with tracing enabled")]
8293
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
8394
async fn run_error_details(

0 commit comments

Comments
 (0)