use dragonfly_client_config::dfinit;
use dragonfly_client_core::{
error::{ErrorType, OrErr},
Error, Result,
};
use tokio::{self, fs};
use toml_edit::{value, Array, ArrayOfTables, Item, Table, Value};
use tracing::{info, instrument};
use url::Url;
#[derive(Debug, Clone)]
pub struct Podman {
config: dfinit::Podman,
proxy_config: dfinit::Proxy,
}
impl Podman {
#[instrument(skip_all)]
pub fn new(config: dfinit::Podman, proxy_config: dfinit::Proxy) -> Self {
Self {
config,
proxy_config,
}
}
#[instrument(skip_all)]
pub async fn run(&self) -> Result<()> {
let mut registries_config_table = toml_edit::DocumentMut::new();
registries_config_table.set_implicit(true);
let mut unqualified_search_registries = Array::default();
for unqualified_search_registry in self.config.unqualified_search_registries.clone() {
unqualified_search_registries.push(Value::from(unqualified_search_registry));
}
registries_config_table.insert(
"unqualified-search-registries",
value(unqualified_search_registries),
);
let proxy_url =
Url::parse(self.proxy_config.addr.as_str()).or_err(ErrorType::ParseError)?;
let proxy_host = proxy_url
.host_str()
.ok_or(Error::Unknown("host not found".to_string()))?;
let proxy_port = proxy_url
.port_or_known_default()
.ok_or(Error::Unknown("port not found".to_string()))?;
let proxy_location = format!("{proxy_host}:{proxy_port}");
let mut registries_table = ArrayOfTables::new();
for registry in self.config.registries.clone() {
info!("add registry: {:?}", registry);
let mut registry_mirror_table = Table::new();
registry_mirror_table.set_implicit(true);
registry_mirror_table.insert("insecure", value(true));
registry_mirror_table.insert("location", value(proxy_location.as_str()));
let mut registry_mirrors_table = ArrayOfTables::new();
registry_mirrors_table.push(registry_mirror_table);
let mut registry_table = Table::new();
registry_table.set_implicit(true);
registry_table.insert("prefix", value(registry.prefix));
registry_table.insert("location", value(registry.location));
registry_table.insert("mirror", Item::ArrayOfTables(registry_mirrors_table));
registries_table.push(registry_table);
}
registries_config_table.insert("registry", Item::ArrayOfTables(registries_table));
let registries_config_dir = self
.config
.config_path
.parent()
.ok_or(Error::Unknown("invalid config path".to_string()))?;
fs::create_dir_all(registries_config_dir.as_os_str()).await?;
fs::write(
self.config.config_path.as_os_str(),
registries_config_table.to_string().as_bytes(),
)
.await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
#[tokio::test]
async fn run_writes_registries_conf() {
let test_cases = vec![
(
vec![dfinit::PodmanRegistry {
prefix: "registry.example.com".into(),
location: "registry.example.com".into(),
}],
vec!["registry.example.com".to_string()],
"http://127.0.0.1:5000",
r#"unqualified-search-registries = ["registry.example.com"]
[[registry]]
prefix = "registry.example.com"
location = "registry.example.com"
[[registry.mirror]]
insecure = true
location = "127.0.0.1:5000"
"#,
),
(
vec![
dfinit::PodmanRegistry {
prefix: "registry.example.com".into(),
location: "registry.example.com".into(),
},
dfinit::PodmanRegistry {
prefix: "docker.io".into(),
location: "registry-1.docker.io".into(),
},
],
vec!["registry.example.com".to_string(), "docker.io".to_string()],
"https://proxy.example.com",
r#"unqualified-search-registries = ["registry.example.com", "docker.io"]
[[registry]]
prefix = "registry.example.com"
location = "registry.example.com"
[[registry.mirror]]
insecure = true
location = "proxy.example.com:443"
[[registry]]
prefix = "docker.io"
location = "registry-1.docker.io"
[[registry.mirror]]
insecure = true
location = "proxy.example.com:443"
"#,
),
(
vec![],
vec![],
"http://127.0.0.1:5000",
"unqualified-search-registries = []\n",
),
];
for (registries, unqualified_search_registries, proxy_addr, expected) in test_cases {
let config_file = NamedTempFile::new().unwrap();
let result = Podman::new(
dfinit::Podman {
config_path: config_file.path().to_path_buf(),
registries,
unqualified_search_registries,
},
dfinit::Proxy {
addr: proxy_addr.into(),
},
)
.run()
.await;
assert!(result.is_ok());
let contents = fs::read_to_string(config_file.path()).await.unwrap();
assert_eq!(contents, expected);
}
}
#[tokio::test]
async fn run_fails_on_invalid_proxy_addr() {
let test_cases = vec![
(
"127.0.0.1:5000",
"ParseError cause: relative URL without a base",
),
("unix:/var/run/dfdaemon.sock", "unknown host not found"),
("dfdaemon://127.0.0.1", "unknown port not found"),
];
for (proxy_addr, expected) in test_cases {
let config_file = NamedTempFile::new().unwrap();
let result = Podman::new(
dfinit::Podman {
config_path: config_file.path().to_path_buf(),
registries: vec![],
unqualified_search_registries: vec![],
},
dfinit::Proxy {
addr: proxy_addr.into(),
},
)
.run()
.await;
assert_eq!(result.unwrap_err().to_string(), expected);
}
}
}