wasmcloud_host/
registry.rs

1use std::collections::{hash_map::Entry, HashMap};
2
3use anyhow::Result;
4use tracing::{debug, instrument, warn};
5use wasmcloud_control_interface::RegistryCredential;
6use wasmcloud_core::{RegistryAuth, RegistryConfig, RegistryType};
7
8use crate::OciConfig;
9
10/// Extension trait to enable converting between registry credentials
11pub trait RegistryCredentialExt {
12    /// Convert a [`RegistryCredential`] to a [`RegistryConfig`]
13    fn into_registry_config(self) -> Result<RegistryConfig>;
14}
15
16impl RegistryCredentialExt for RegistryCredential {
17    fn into_registry_config(self) -> Result<RegistryConfig> {
18        RegistryConfig::builder()
19            .reg_type(match self.registry_type() {
20                "oci" => RegistryType::Oci,
21                registry_type => {
22                    warn!(%registry_type, "unknown registry type, defaulting to OCI");
23                    RegistryType::Oci
24                }
25            })
26            .auth(match (self.username(), self.password(), self.token()) {
27                (Some(username), Some(password), _) => RegistryAuth::Basic(username.into(), password.into()),
28                (None, None, Some(token)) => RegistryAuth::Token(token.into()),
29                (None, None, None) => RegistryAuth::Anonymous,
30                (_, _, _) => {
31                    warn!("invalid combination of registry credentials, defaulting to no authentication");
32                    RegistryAuth::Anonymous
33                }
34            })
35            .allow_latest(false)
36            .allow_insecure(false)
37            .additional_ca_paths(Vec::new())
38            .build()
39    }
40}
41
42#[derive(Debug, Default)]
43/// A struct to hold supplemental [RegistryConfig] for the host.
44pub struct SupplementalConfig {
45    /// A map of registry URLs to [RegistryConfig] to use for it when
46    /// fetching images from OCI registries.
47    pub registry_config: Option<HashMap<String, RegistryConfig>>,
48}
49
50/// A helper function to merge [crate::oci::Config] into the given registry configuration
51#[instrument(level = "debug", skip_all)]
52pub async fn merge_registry_config(
53    registry_config: &mut HashMap<String, RegistryConfig>,
54    oci_opts: OciConfig,
55) {
56    let allow_latest = oci_opts.allow_latest;
57    let additional_ca_paths = oci_opts.additional_ca_paths;
58
59    // update auth for specific registry, if provided
60    if let Some(reg) = oci_opts.oci_registry {
61        match registry_config.entry(reg.clone()) {
62            Entry::Occupied(_entry) => {
63                // note we don't update config here, since the config service should take priority
64                warn!(oci_registry_url = %reg, "ignoring OCI registry config, overridden by config service");
65            }
66            Entry::Vacant(entry) => {
67                debug!(oci_registry_url = %reg, "set registry config");
68                entry.insert(
69                    RegistryConfig::builder()
70                        .reg_type(RegistryType::Oci)
71                        .auth(RegistryAuth::from((
72                            oci_opts.oci_user,
73                            oci_opts.oci_password,
74                        )))
75                        .build()
76                        .expect("failed to build registry config"),
77                );
78            }
79        }
80    }
81
82    // update or create entry for all registries in allowed_insecure
83    oci_opts.allowed_insecure.into_iter().for_each(|reg| {
84        match registry_config.entry(reg.clone()) {
85            Entry::Occupied(mut entry) => {
86                debug!(oci_registry_url = %reg, "set allowed_insecure");
87                entry.get_mut().set_allow_insecure(true);
88            }
89            Entry::Vacant(entry) => {
90                debug!(oci_registry_url = %reg, "set allowed_insecure");
91                entry.insert(
92                    RegistryConfig::builder()
93                        .reg_type(RegistryType::Oci)
94                        .allow_insecure(true)
95                        .build()
96                        .expect("failed to build registry config"),
97                );
98            }
99        }
100    });
101
102    // update allow_latest for all registries
103    registry_config.iter_mut().for_each(|(url, config)| {
104        if !additional_ca_paths.is_empty() {
105            config.set_additional_ca_paths(additional_ca_paths.clone());
106        }
107        if allow_latest {
108            debug!(oci_registry_url = %url, "set allow_latest");
109        }
110        config.set_allow_latest(allow_latest);
111    });
112}