Commit

Author:

Hash:

Timestamp:

+70 -35 +/-5 browse

Kevin Schoon [me@kevinschoon.com]

d9830ced413d1a37e010c45062465a62015dd738

Mon, 14 Apr 2025 14:18:57 +0000 (1 month ago)

clean up examples, add write manifest
1diff --git a/examples/custom.rs b/examples/custom.rs
2index 21cfe35..a1265be 100644
3--- a/examples/custom.rs
4+++ b/examples/custom.rs
5 @@ -1,6 +1,7 @@
6- use std::{error::Error, path::Path, sync::Arc};
7+ use futures::StreamExt;
8+ use std::{error::Error, path::Path, str::FromStr, sync::Arc};
9
10- use papyri::{oci_interface::OciInterface, storage};
11+ use papyri::{Digest, Namespace, oci_interface::OciInterface, storage};
12
13 #[tokio::main]
14 async fn main() -> Result<(), Box<dyn Error>> {
15 @@ -8,8 +9,22 @@ async fn main() -> Result<(), Box<dyn Error>> {
16 base: Path::new("registry").to_path_buf(),
17 };
18 fs.init()?;
19- let _ = OciInterface {
20+ let oci_interface = OciInterface {
21 storage: Arc::new(fs.inner()),
22 };
23+ let namespace = &Namespace::from_str("hello/world")?;
24+ let digest = &Digest::try_from(
25+ "sha256:a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447",
26+ )
27+ .unwrap();
28+ oci_interface
29+ .write_blob(namespace, digest, "hello world!".as_bytes())
30+ .await?;
31+ let mut stream = oci_interface.read_blob(digest).await?;
32+ let blob = stream.next().await.unwrap()?;
33+ let blob = blob.to_vec();
34+ let message = String::from_utf8_lossy(blob.as_slice());
35+ println!("{}", message);
36+ assert!(message == "hello world!");
37 Ok(())
38 }
39 diff --git a/examples/server.rs b/examples/server.rs
40index 851913d..35b6a36 100644
41--- a/examples/server.rs
42+++ b/examples/server.rs
43 @@ -1,10 +1,10 @@
44- use std::{collections::HashMap, error::Error, path::Path, str::FromStr};
45+ use std::{error::Error, path::Path, str::FromStr};
46
47 use axum::{Router, extract::Request};
48 use papyri::storage::Storage;
49 use tower::Layer;
50 use tower_http::{normalize_path::NormalizePathLayer, trace::TraceLayer};
51- use tracing::{event, info_span, trace, Level};
52+ use tracing::{Level, event, info_span};
53
54 const ADDRESS: &str = "127.0.0.1:8700";
55
56 @@ -30,22 +30,26 @@ async fn main() -> Result<(), Box<dyn Error>> {
57 let registry = papyri::axum::router(&fs);
58 let middleware = tower::util::MapRequestLayer::new(papyri::axum::extract_namespace);
59
60- let router =
61- Router::new()
62- .nest_service("/v2/", middleware.layer(registry))
63- .layer(TraceLayer::new_for_http().make_span_with(
64- |req: &Request<_>| {
65- let headers: Vec<(String, String)> = req.headers().iter().map(|(key, value)| (key.to_string(), value.to_str().unwrap().to_string())).collect();
66- let span = info_span!("http_request", method = ?req.method(), uri = req.uri().to_string());
67- span.in_scope(|| {
68- headers.iter().for_each(|(key, value)| {
69+ let router = Router::new()
70+ .nest_service("/v2/", middleware.layer(registry))
71+ .layer(
72+ TraceLayer::new_for_http().make_span_with(|req: &Request<_>| {
73+ let headers: Vec<(String, String)> = req
74+ .headers()
75+ .iter()
76+ .map(|(key, value)| (key.to_string(), value.to_str().unwrap().to_string()))
77+ .collect();
78+ let span =
79+ info_span!("http_request", method = ?req.method(), uri = req.uri().to_string());
80+ span.in_scope(|| {
81+ headers.iter().for_each(|(key, value)| {
82 event!(Level::DEBUG, key = key, value = value);
83- })
84- });
85- span
86- },
87- ))
88- .layer(NormalizePathLayer::trim_trailing_slash());
89+ })
90+ });
91+ span
92+ }),
93+ )
94+ .layer(NormalizePathLayer::trim_trailing_slash());
95
96 axum::serve(listener, router).await?;
97 Ok(())
98 diff --git a/src/axum/handlers_manifest.rs b/src/axum/handlers_manifest.rs
99index 13fefb5..60cc1a5 100644
100--- a/src/axum/handlers_manifest.rs
101+++ b/src/axum/handlers_manifest.rs
102 @@ -1,16 +1,13 @@
103- use std::{str::FromStr, sync::Arc};
104+ use std::sync::Arc;
105
106 use axum::{
107 Extension, Json,
108- extract::{FromRequest, Path, Request, State},
109+ extract::{FromRequest, Request, State},
110 response::{IntoResponse, Response},
111 };
112 use bytes::{Buf, Bytes};
113 use http::{StatusCode, header::CONTENT_TYPE};
114- use oci_spec::{
115- distribution::Reference,
116- image::{ImageManifest, MediaType},
117- };
118+ use oci_spec::image::{ImageManifest, MediaType};
119
120 use crate::Namespace;
121
122 diff --git a/src/lib.rs b/src/lib.rs
123index 7ab9579..46c898f 100644
124--- a/src/lib.rs
125+++ b/src/lib.rs
126 @@ -1,7 +1,7 @@
127 use std::{fmt::Display, str::FromStr};
128
129 use error::Error;
130- use oci_spec::image::Digest;
131+ pub use oci_spec::image::Digest;
132 use regex::Regex;
133 use relative_path::RelativePath;
134
135 diff --git a/src/oci_interface.rs b/src/oci_interface.rs
136index 5f76889..d6b087b 100644
137--- a/src/oci_interface.rs
138+++ b/src/oci_interface.rs
139 @@ -2,10 +2,7 @@ use std::{pin::Pin, str::FromStr, sync::Arc};
140
141 use bytes::Bytes;
142 use futures::{Stream, StreamExt};
143- use oci_spec::{
144- distribution::TagList,
145- image::{Digest, ImageManifest},
146- };
147+ use oci_spec::image::{Digest, ImageManifest};
148 use sha2::{Digest as HashDigest, Sha256};
149 use uuid::Uuid;
150
151 @@ -13,7 +10,7 @@ use crate::{
152 Namespace, TagOrDigest,
153 address::Address,
154 error::Error,
155- storage::{InnerStream, Storage, StorageIface},
156+ storage::{InnerStream, StorageIface},
157 };
158
159 use base64::prelude::*;
160 @@ -28,7 +25,10 @@ pub struct OciInterface {
161 }
162
163 impl OciInterface {
164- async fn resolve_link<'a>(&self, link: &Address<'a>) -> Result<Digest, Error> {
165+ async fn resolve_link(&self, link: &Address<'_>) -> Result<Digest, Error> {
166+ if !link.is_link() {
167+ panic!("Address is not a link: {}", link);
168+ }
169 let digest_bytes = self
170 .storage
171 .read_bytes(link)
172 @@ -99,6 +99,25 @@ impl OciInterface {
173 Ok(())
174 }
175
176+ pub async fn write_blob(
177+ &self,
178+ namespace: &Namespace,
179+ digest: &Digest,
180+ content: &[u8],
181+ ) -> Result<(), Error> {
182+ let uuid = &Uuid::new_v4();
183+ let tmp_blob_addr = &Address::TempBlob { namespace, uuid };
184+ self.storage
185+ .write_all(tmp_blob_addr, content)
186+ .await
187+ .map_err(Error::Storage)?;
188+ self.storage
189+ .mv(tmp_blob_addr, &Address::Blob { digest })
190+ .await
191+ .map_err(Error::Storage)?;
192+ Ok(())
193+ }
194+
195 pub async fn write_manifest(
196 &self,
197 namespace: &Namespace,
198 @@ -112,7 +131,7 @@ impl OciInterface {
199 namespace,
200 };
201 self.storage
202- .write_all(&tmp_blob_addr, manifest_bytes.to_vec().as_slice())
203+ .write_all(tmp_blob_addr, manifest_bytes.to_vec().as_slice())
204 .await
205 .map_err(Error::Storage)?;
206 // TODO: Generalize in storage.rs
207 @@ -121,7 +140,7 @@ impl OciInterface {
208 let digest = Digest::from_str(&format!("sha256:{}", hash_str)).unwrap();
209 let blob_address = &Address::Blob { digest: &digest };
210 self.storage
211- .mv(&tmp_blob_addr, blob_address)
212+ .mv(tmp_blob_addr, blob_address)
213 .await
214 .map_err(Error::Storage)?;
215 self.storage