Skip to content

Commit b637a13

Browse files
committed
Added cleanup routines / Cosign update
1 parent c1e576d commit b637a13

File tree

2 files changed

+106
-14
lines changed

2 files changed

+106
-14
lines changed

tools/monitor-oci-artifacts/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ ARG RELEASE="1"
88
# Update image
99
RUN microdnf install -y yum \
1010
&& yum -y update-minimal --security --sec-severity=Important --sec-severity=Critical \
11-
&& curl -L -O https://github.com/sigstore/cosign/releases/download/v2.1.1/cosign-2.1.1.x86_64.rpm \
12-
&& rpm -ivh cosign-2.1.1.x86_64.rpm \
11+
&& curl -L -O https://github.com/sigstore/cosign/releases/download/v2.2.3/cosign-2.2.3-1.x86_64.rpm \
12+
&& rpm -ivh cosign-2.2.3-1.x86_64.rpm \
1313
&& yum clean all \
1414
&& microdnf clean all
1515

tools/monitor-oci-artifacts/src/main.rs

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,24 @@ impl std::ops::Deref for TagList {
4343
}
4444
}
4545

46+
#[derive(Deserialize, Debug)]
47+
struct ArtifactReference {
48+
child_digest: String,
49+
}
4650
#[derive(Deserialize, Debug)]
4751
struct Artifact {
4852
digest: String,
4953
manifest_media_type: String,
5054
media_type: String,
5155
tags: Option<TagList>,
56+
references: Option<Vec<ArtifactReference>>,
5257
}
5358

5459
#[tokio::main]
5560
async fn main() -> Result<(), Box<dyn std::error::Error>> {
5661
let registry_hostname = "oci.stackable.tech";
5762
let base_url = format!("https://{}/api/v2.0", registry_hostname);
58-
let page_size = 10;
63+
let page_size = 20;
5964
let mut page = 1;
6065
let attestation_tag_regex = Regex::new(r"^sha256-[0-9a-f]{64}.att$").unwrap();
6166

@@ -73,15 +78,33 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
7378
if project_name == "sandbox" {
7479
continue;
7580
}
76-
let artifacts: Vec<Artifact> = reqwest::get(format!(
77-
"{}/projects/{}/repositories/{}/artifacts",
78-
base_url,
79-
encode(project_name),
80-
encode(repository_name)
81-
))
82-
.await?
83-
.json()
84-
.await?;
81+
82+
let mut attestations: Vec<String> = Vec::with_capacity(32);
83+
let mut potentially_attested_artifacts: Vec<&str> = Vec::with_capacity(32);
84+
let mut artifacts: Vec<Artifact> = Vec::with_capacity(64);
85+
let mut page = 1;
86+
let page_size = 20;
87+
loop {
88+
let artifacts_page: Vec<Artifact> = reqwest::get(format!(
89+
"{}/projects/{}/repositories/{}/artifacts?page_size={}&page={}",
90+
base_url,
91+
encode(project_name),
92+
encode(repository_name),
93+
page_size,
94+
page
95+
))
96+
.await?
97+
.json()
98+
.await?;
99+
100+
let number_of_returned_artifacts = artifacts_page.len();
101+
artifacts.extend(artifacts_page);
102+
if number_of_returned_artifacts < page_size {
103+
break;
104+
}
105+
page += 1;
106+
}
107+
85108
for artifact in &artifacts {
86109
if artifact
87110
.tags
@@ -99,15 +122,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
99122
{
100123
// it's an attestation, attestations artifacts themselves are not signed
101124
println!(
102-
"skipping attestation {}{} ({})",
125+
"skipping attestation {} {} ({})",
103126
repository_name,
104127
artifact.digest,
105128
artifact.tags.as_ref().unwrap()
106129
);
130+
attestations.push(artifact.tags.as_ref().unwrap()[0].name.clone());
107131
continue;
132+
} else {
133+
potentially_attested_artifacts.push(&artifact.digest[7..71]);
108134
}
109135

110-
if project_name == "sdp" && (repository_name != "git-sync" && repository_name != "ubi8-rust-builder") {
136+
if project_name == "sdp"
137+
&& (repository_name != "git-sync" && repository_name != "ubi8-rust-builder")
138+
{
111139
if artifact.manifest_media_type
112140
!= "application/vnd.docker.distribution.manifest.v2+json"
113141
{
@@ -159,6 +187,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
159187
exit(cmd_output.status.code().unwrap_or(1));
160188
}
161189
}
190+
191+
// remove dangling attestations
192+
for attestation in attestations {
193+
if !potentially_attested_artifacts.contains(&&attestation[7..71]) {
194+
println!("removing dangling attestation {}", attestation);
195+
reqwest::Client::new()
196+
.delete(format!(
197+
"{}/projects/{}/repositories/{}/artifacts/{}",
198+
base_url,
199+
encode(project_name),
200+
encode(repository_name),
201+
attestation
202+
))
203+
.basic_auth("robot$stackable-cleanup", Some("XX"))
204+
.send()
205+
.await?;
206+
}
207+
}
162208
}
163209

164210
if repositories.len() < page_size {
@@ -168,5 +214,51 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
168214
page += 1;
169215
}
170216

217+
let latest_rust_builder_artifact: Artifact = reqwest::Client::new()
218+
.get(format!(
219+
"{}/projects/sdp/repositories/ubi8-rust-builder/artifacts/latest",
220+
base_url,
221+
))
222+
.send()
223+
.await?
224+
.json()
225+
.await?;
226+
227+
let referenced_digests = latest_rust_builder_artifact
228+
.references
229+
.unwrap()
230+
.into_iter()
231+
.map(|reference| reference.child_digest)
232+
.collect::<Vec<String>>();
233+
234+
let rust_builder_artifacts: Vec<Artifact> = reqwest::get(format!(
235+
"{}/projects/sdp/repositories/ubi8-rust-builder/artifacts?page_size=100",
236+
base_url
237+
))
238+
.await?
239+
.json()
240+
.await?;
241+
242+
// keep "latest" and its referenced artifacts
243+
for artifact in rust_builder_artifacts {
244+
if artifact.digest != latest_rust_builder_artifact.digest
245+
&& !referenced_digests.contains(&artifact.digest)
246+
{
247+
println!(
248+
"removing dangling rust builder artifact {}",
249+
artifact.digest
250+
);
251+
reqwest::Client::new()
252+
.delete(format!(
253+
"{}/projects/sdp/repositories/ubi8-rust-builder/artifacts/{}",
254+
base_url, artifact.digest
255+
))
256+
.basic_auth("robot$stackable-cleanup", Some("XX"))
257+
.send()
258+
.await?;
259+
}
260+
}
261+
171262
Ok(())
172263
}
264+
// cachebust

0 commit comments

Comments
 (0)