Skip to main content

microsandbox_image/registry/
builder.rs

1use oci_client::{
2    Client,
3    client::{Certificate, CertificateEncoding, ClientConfig, ClientProtocol},
4};
5
6use crate::{
7    auth::RegistryAuth,
8    cache::GlobalCache,
9    error::{ImageError, ImageResult},
10    platform::Platform,
11};
12
13use super::client::{Registry, resolve_platform_digest};
14
15//--------------------------------------------------------------------------------------------------
16// Types
17//--------------------------------------------------------------------------------------------------
18
19/// Builder for constructing a [`Registry`] client with optional auth and TLS settings.
20pub struct RegistryBuilder {
21    pub(super) platform: Platform,
22    pub(super) cache: GlobalCache,
23    pub(super) auth: oci_client::secrets::RegistryAuth,
24    pub(super) insecure_registries: Vec<String>,
25    pub(super) extra_ca_certs: Vec<Vec<u8>>,
26}
27
28//--------------------------------------------------------------------------------------------------
29// Methods
30//--------------------------------------------------------------------------------------------------
31
32impl RegistryBuilder {
33    /// Create a registry builder with anonymous authentication and default TLS settings.
34    pub(crate) fn new(platform: Platform, cache: GlobalCache) -> Self {
35        Self {
36            platform,
37            cache,
38            auth: oci_client::secrets::RegistryAuth::Anonymous,
39            insecure_registries: Vec::new(),
40            extra_ca_certs: Vec::new(),
41        }
42    }
43
44    /// Set authentication credentials for the registry.
45    pub fn auth(mut self, auth: RegistryAuth) -> Self {
46        self.auth = (&auth).into();
47        self
48    }
49
50    /// Add registries that should be accessed over plain HTTP instead of HTTPS.
51    pub fn add_insecure_registries(mut self, registries: Vec<String>) -> Self {
52        self.insecure_registries.extend(registries);
53        self
54    }
55
56    /// Add PEM-encoded CA root certificates to trust.
57    pub fn extra_ca_certs(mut self, certs: Vec<Vec<u8>>) -> Self {
58        self.extra_ca_certs = certs;
59        self
60    }
61
62    /// Build the registry client.
63    ///
64    /// Returns [`ImageError::InvalidCertificate`] if any PEM data in
65    /// `extra_ca_certs` cannot be parsed as valid certificates.
66    pub fn build(self) -> ImageResult<Registry> {
67        let protocol = if self.insecure_registries.is_empty() {
68            ClientProtocol::Https
69        } else {
70            ClientProtocol::HttpsExcept(self.insecure_registries)
71        };
72
73        let mut extra_root_certificates = Vec::new();
74        for (i, pem_data) in self.extra_ca_certs.into_iter().enumerate() {
75            let certs: Vec<_> = rustls_pemfile::certs(&mut pem_data.as_slice())
76                .collect::<Result<_, _>>()
77                .map_err(|e| {
78                    ImageError::InvalidCertificate(format!("entry {i}: failed to parse: {e}"))
79                })?;
80
81            if certs.is_empty() {
82                return Err(ImageError::InvalidCertificate(format!(
83                    "entry {i}: no certificates found in PEM data"
84                )));
85            }
86
87            for cert in certs {
88                extra_root_certificates.push(Certificate {
89                    encoding: CertificateEncoding::Der,
90                    data: cert.to_vec(),
91                });
92            }
93        }
94
95        let platform = self.platform.clone();
96        let client = Client::new(ClientConfig {
97            protocol,
98            extra_root_certificates,
99            platform_resolver: Some(Box::new(move |manifests| {
100                resolve_platform_digest(manifests, &platform)
101            })),
102            ..Default::default()
103        });
104
105        Ok(Registry {
106            client,
107            auth: self.auth,
108            platform: self.platform,
109            cache: self.cache,
110        })
111    }
112}