prvng_platform/crates/extension-registry/src/service.rs

216 lines
5.9 KiB
Rust
Raw Normal View History

//! Core extension registry service implementation
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use sha2::Digest;
use tokio::sync::RwLock;
/// OCI image manifest
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageManifest {
/// Schema version
pub schema_version: u32,
/// Media type
pub media_type: String,
/// Image config digest (sha256:...)
pub config: ManifestConfig,
/// Image layers
pub layers: Vec<Layer>,
}
/// Image configuration reference
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManifestConfig {
/// Digest (sha256:...)
pub digest: String,
/// Size in bytes
pub size: u64,
/// Media type
pub media_type: String,
}
/// Image layer
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Layer {
/// Layer digest (sha256:...)
pub digest: String,
/// Size in bytes
pub size: u64,
/// Media type
pub media_type: String,
}
/// Push blob request
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PushBlobRequest {
/// Blob content
pub content: Vec<u8>,
/// Content digest for validation
pub digest: String,
}
/// Push blob response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PushBlobResponse {
/// Blob digest
pub digest: String,
/// Blob size in bytes
pub size: u64,
}
/// Pull manifest response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PullManifestResponse {
/// Image manifest
pub manifest: ImageManifest,
/// Manifest digest
pub digest: String,
/// Content type
pub content_type: String,
}
/// Extension metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtensionMetadata {
/// Extension name
pub name: String,
/// Extension version
pub version: String,
/// Extension description
pub description: String,
/// Supported platforms (e.g., ["linux/amd64", "linux/arm64"])
pub platforms: Vec<String>,
}
/// Core extension registry service
pub struct ExtensionRegistry {
/// Server address
addr: SocketAddr,
/// Stored blobs (digest -> content)
blobs: Arc<RwLock<HashMap<String, Vec<u8>>>>,
/// Stored manifests (repo:tag -> manifest)
manifests: Arc<RwLock<HashMap<String, ImageManifest>>>,
/// Extension metadata registry
extensions: Arc<RwLock<HashMap<String, ExtensionMetadata>>>,
}
impl ExtensionRegistry {
/// Create new extension registry
pub fn new(addr: SocketAddr) -> Self {
Self {
addr,
blobs: Arc::new(RwLock::new(HashMap::new())),
manifests: Arc::new(RwLock::new(HashMap::new())),
extensions: Arc::new(RwLock::new(HashMap::new())),
}
}
/// Get service address
pub fn addr(&self) -> SocketAddr {
self.addr
}
/// Check if blob exists
pub async fn blob_exists(&self, digest: &str) -> Result<bool> {
let blobs = self.blobs.read().await;
Ok(blobs.contains_key(digest))
}
/// Push blob to registry
pub async fn push_blob(&self, req: PushBlobRequest) -> Result<PushBlobResponse> {
let size = req.content.len() as u64;
let mut blobs = self.blobs.write().await;
blobs.insert(req.digest.clone(), req.content);
Ok(PushBlobResponse {
digest: req.digest,
size,
})
}
/// Pull blob from registry
pub async fn pull_blob(&self, digest: &str) -> Result<Vec<u8>> {
let blobs = self.blobs.read().await;
blobs
.get(digest)
.cloned()
.ok_or_else(|| anyhow::anyhow!("Blob not found: {}", digest))
}
/// Check if manifest exists
pub async fn manifest_exists(&self, reference: &str) -> Result<bool> {
let manifests = self.manifests.read().await;
Ok(manifests.contains_key(reference))
}
/// Put image manifest
pub async fn put_manifest(&self, reference: String, manifest: ImageManifest) -> Result<String> {
let digest = format!(
"sha256:{}",
hex::encode(sha2::Sha256::digest(&serde_json::to_vec(&manifest)?))
);
let mut manifests = self.manifests.write().await;
manifests.insert(reference, manifest);
Ok(digest)
}
/// Get image manifest
pub async fn get_manifest(&self, reference: &str) -> Result<PullManifestResponse> {
let manifests = self.manifests.read().await;
let manifest = manifests
.get(reference)
.cloned()
.ok_or_else(|| anyhow::anyhow!("Manifest not found: {}", reference))?;
let digest = format!(
"sha256:{}",
hex::encode(sha2::Sha256::digest(&serde_json::to_vec(&manifest)?))
);
Ok(PullManifestResponse {
manifest,
digest,
content_type: "application/vnd.docker.distribution.manifest.v2+json".to_string(),
})
}
/// Register extension
pub async fn register_extension(&self, metadata: ExtensionMetadata) -> Result<()> {
let mut extensions = self.extensions.write().await;
extensions.insert(metadata.name.clone(), metadata);
Ok(())
}
/// Get extension metadata
pub async fn get_extension(&self, name: &str) -> Result<ExtensionMetadata> {
let extensions = self.extensions.read().await;
extensions
.get(name)
.cloned()
.ok_or_else(|| anyhow::anyhow!("Extension not found: {}", name))
}
/// List all extensions
pub async fn list_extensions(&self) -> Result<Vec<ExtensionMetadata>> {
let extensions = self.extensions.read().await;
Ok(extensions.values().cloned().collect())
}
/// Health check endpoint
pub async fn health_check(&self) -> Result<()> {
Ok(())
}
}
impl Default for ExtensionRegistry {
fn default() -> Self {
Self::new("127.0.0.1:8084".parse().unwrap())
}
}