tangled
alpha
login
or
join now
pierrelf.com
/
grain
0
fork
atom
Rust implementation of OCI Distribution Spec with granular access control
0
fork
atom
overview
issues
9
pulls
pipelines
fix gc tests
pierrelf.com
4 months ago
6fbf98a5
b813c443
+15
-8
2 changed files
expand all
collapse all
unified
split
src
gc.rs
tests
gc_operations.rs
+14
-7
src/gc.rs
···
3
3
use std::path::Path;
4
4
use std::time::{SystemTime, UNIX_EPOCH};
5
5
6
6
-
type BlobInfo = (String, String, u64); // (org, repo, size)
6
6
+
type BlobLocation = (String, String, u64); // (org, repo, size)
7
7
type UnreferencedBlob = (String, String, String, u64); // (org, repo, digest, size)
8
8
9
9
#[derive(Debug, Serialize, Deserialize)]
···
153
153
/// Scan all blobs in storage
154
154
fn scan_all_blobs(
155
155
stats: &mut GcStats,
156
156
-
) -> Result<HashMap<String, BlobInfo>, Box<dyn std::error::Error>> {
157
157
-
let mut all_blobs = HashMap::new(); // digest -> (org, repo, size)
156
156
+
) -> Result<HashMap<String, Vec<BlobLocation>>, Box<dyn std::error::Error>> {
157
157
+
let mut all_blobs: HashMap<String, Vec<BlobLocation>> = HashMap::new();
158
158
let blobs_dir = Path::new("./tmp/blobs");
159
159
160
160
if !blobs_dir.exists() {
···
188
188
let digest = blob_entry.file_name().to_string_lossy().to_string();
189
189
let size = blob_entry.metadata()?.len();
190
190
191
191
-
all_blobs.insert(digest.clone(), (org.clone(), repo.clone(), size));
191
191
+
// Track all locations for this digest
192
192
+
all_blobs
193
193
+
.entry(digest)
194
194
+
.or_default()
195
195
+
.push((org.clone(), repo.clone(), size));
192
196
}
193
197
}
194
198
}
···
198
202
199
203
/// Mark unreferenced blobs for deletion
200
204
fn mark_unreferenced_blobs(
201
201
-
all_blobs: &HashMap<String, BlobInfo>,
205
205
+
all_blobs: &HashMap<String, Vec<BlobLocation>>,
202
206
referenced_blobs: &HashSet<String>,
203
207
) -> Result<Vec<UnreferencedBlob>, Box<dyn std::error::Error>> {
204
208
let mut unreferenced = Vec::new();
205
209
206
206
-
for (digest, (org, repo, size)) in all_blobs {
210
210
+
for (digest, locations) in all_blobs {
207
211
if !referenced_blobs.contains(digest) {
208
208
-
unreferenced.push((org.clone(), repo.clone(), digest.clone(), *size));
212
212
+
// Add all locations of this unreferenced blob
213
213
+
for (org, repo, size) in locations {
214
214
+
unreferenced.push((org.clone(), repo.clone(), digest.clone(), *size));
215
215
+
}
209
216
}
210
217
}
211
218
+1
-1
tests/gc_operations.rs
···
56
56
let result: serde_json::Value = resp.json().unwrap();
57
57
58
58
assert!(result["blobs_scanned"].as_u64().unwrap() >= 2);
59
59
-
assert!(result["blobs_to_delete"].as_u64().unwrap() >= 1);
59
59
+
assert!(result["blobs_unreferenced"].as_u64().unwrap() >= 1);
60
60
61
61
// Verify orphaned blob still exists (dry-run)
62
62
let resp = client