feat(container_runtime): support podman container runtime (#812)
Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
parent
f43ce9ec47
commit
07591fa862
|
|
@ -50,3 +50,6 @@ Temporary Items
|
|||
|
||||
# Ignore unuseful files
|
||||
scripts/certs
|
||||
|
||||
# Ignore .vscode folder
|
||||
.vscode
|
||||
|
|
|
|||
|
|
@ -1007,6 +1007,7 @@ dependencies = [
|
|||
"dragonfly-client",
|
||||
"dragonfly-client-config",
|
||||
"dragonfly-client-core",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"toml",
|
||||
"toml_edit",
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ fn default_container_runtime_crio_config_path() -> PathBuf {
|
|||
PathBuf::from("/etc/containers/registries.conf")
|
||||
}
|
||||
|
||||
/// default_container_runtime_podman_config_path is the default podman configuration path.
|
||||
#[inline]
|
||||
fn default_container_runtime_podman_config_path() -> PathBuf {
|
||||
PathBuf::from("/etc/containers/registries.conf")
|
||||
}
|
||||
|
||||
/// default_container_runtime_crio_unqualified_search_registries is the default unqualified search registries of cri-o,
|
||||
/// refer to https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#global-settings.
|
||||
#[inline]
|
||||
|
|
@ -67,6 +73,17 @@ fn default_container_runtime_crio_unqualified_search_registries() -> Vec<String>
|
|||
]
|
||||
}
|
||||
|
||||
/// default_container_runtime_podman_unqualified_search_registries is the default unqualified search registries of cri-o,
|
||||
/// refer to https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#global-settings.
|
||||
#[inline]
|
||||
fn default_container_runtime_podman_unqualified_search_registries() -> Vec<String> {
|
||||
vec![
|
||||
"registry.fedoraproject.org".to_string(),
|
||||
"registry.access.redhat.com".to_string(),
|
||||
"docker.io".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
/// default_proxy_addr is the default proxy address of dfdaemon.
|
||||
#[inline]
|
||||
fn default_proxy_addr() -> String {
|
||||
|
|
@ -156,6 +173,37 @@ pub struct CRIO {
|
|||
pub registries: Vec<CRIORegistry>,
|
||||
}
|
||||
|
||||
/// CRIORegistry is the registry configuration for cri-o.
|
||||
#[derive(Debug, Clone, Default, Validate, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct PodmanRegistry {
|
||||
/// prefix is the prefix of the user-specified image name, refer to
|
||||
/// https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#choosing-a-registry-toml-table.
|
||||
pub prefix: String,
|
||||
|
||||
/// location accepts the same format as the prefix field, and specifies the physical location of the prefix-rooted namespace,
|
||||
/// refer to https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#remapping-and-mirroring-registries.
|
||||
pub location: String,
|
||||
}
|
||||
|
||||
/// Podman is the podman configuration for dfinit.
|
||||
#[derive(Debug, Clone, Default, Validate, Deserialize, Serialize)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct Podman {
|
||||
/// config_path is the path of cri-o registries's configuration file.
|
||||
#[serde(default = "default_container_runtime_podman_config_path")]
|
||||
pub config_path: PathBuf,
|
||||
|
||||
/// unqualified_search_registries is an array of host[:port] registries to try when pulling an unqualified image, in order.
|
||||
/// Refer to https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#global-settings.
|
||||
#[serde(default = "default_container_runtime_podman_unqualified_search_registries")]
|
||||
pub unqualified_search_registries: Vec<String>,
|
||||
|
||||
/// registries is the list of cri-o registries, refer to
|
||||
/// https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md#namespaced-registry-settings.
|
||||
pub registries: Vec<PodmanRegistry>,
|
||||
}
|
||||
|
||||
/// Docker is the docker configuration for dfinit.
|
||||
#[derive(Debug, Clone, Default, Validate, Deserialize, Serialize)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
|
|
@ -179,6 +227,7 @@ pub enum ContainerRuntimeConfig {
|
|||
Containerd(Containerd),
|
||||
Docker(Docker),
|
||||
CRIO(CRIO),
|
||||
Podman(Podman),
|
||||
}
|
||||
|
||||
/// Serialize is the implementation of the Serialize trait for ContainerRuntimeConfig.
|
||||
|
|
@ -203,6 +252,11 @@ impl Serialize for ContainerRuntimeConfig {
|
|||
state.serialize_field("crio", &cfg)?;
|
||||
state.end()
|
||||
}
|
||||
ContainerRuntimeConfig::Podman(ref cfg) => {
|
||||
let mut state = serializer.serialize_struct("podman", 1)?;
|
||||
state.serialize_field("podman", &cfg)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,6 +272,7 @@ impl<'de> Deserialize<'de> for ContainerRuntimeConfig {
|
|||
containerd: Option<Containerd>,
|
||||
docker: Option<Docker>,
|
||||
crio: Option<CRIO>,
|
||||
podman: Option<Podman>,
|
||||
}
|
||||
|
||||
let helper = ContainerRuntimeHelper::deserialize(deserializer)?;
|
||||
|
|
@ -233,9 +288,15 @@ impl<'de> Deserialize<'de> for ContainerRuntimeConfig {
|
|||
ContainerRuntimeHelper {
|
||||
crio: Some(crio), ..
|
||||
} => Ok(ContainerRuntimeConfig::CRIO(crio)),
|
||||
ContainerRuntimeHelper {
|
||||
podman: Some(podman),
|
||||
..
|
||||
} => Ok(ContainerRuntimeConfig::Podman(podman)),
|
||||
_ => {
|
||||
use serde::de::Error;
|
||||
Err(D::Error::custom("expected containerd or docker or crio"))
|
||||
Err(D::Error::custom(
|
||||
"expected containerd or docker or crio or podman",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,3 +25,4 @@ tracing.workspace = true
|
|||
toml_edit.workspace = true
|
||||
toml.workspace = true
|
||||
url.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use tracing::{info, instrument};
|
|||
pub mod containerd;
|
||||
pub mod crio;
|
||||
pub mod docker;
|
||||
pub mod podman;
|
||||
|
||||
/// Engine represents config of the container runtime engine.
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -28,6 +29,7 @@ enum Engine {
|
|||
Containerd(containerd::Containerd),
|
||||
Docker(docker::Docker),
|
||||
Crio(crio::CRIO),
|
||||
Podman(podman::Podman),
|
||||
}
|
||||
|
||||
/// ContainerRuntime represents the container runtime manager.
|
||||
|
|
@ -55,6 +57,7 @@ impl ContainerRuntime {
|
|||
Some(Engine::Containerd(containerd)) => containerd.run().await,
|
||||
Some(Engine::Docker(docker)) => docker.run().await,
|
||||
Some(Engine::Crio(crio)) => crio.run().await,
|
||||
Some(Engine::Podman(podman)) => podman.run().await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,6 +75,9 @@ impl ContainerRuntime {
|
|||
ContainerRuntimeConfig::CRIO(crio) => {
|
||||
Engine::Crio(crio::CRIO::new(crio.clone(), config.proxy.clone()))
|
||||
}
|
||||
ContainerRuntimeConfig::Podman(podman) => {
|
||||
Engine::Podman(podman::Podman::new(podman.clone(), config.proxy.clone()))
|
||||
}
|
||||
};
|
||||
|
||||
info!("container runtime engine is {:?}", engine);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright 2024 The Dragonfly Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/// Podman represents the podman runtime manager.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Podman {
|
||||
/// config is the configuration for initializing
|
||||
/// runtime environment for the dfdaemon.
|
||||
config: dfinit::Podman,
|
||||
|
||||
/// proxy_config is the configuration for the dfdaemon's proxy server.
|
||||
proxy_config: dfinit::Proxy,
|
||||
}
|
||||
|
||||
/// Podman implements the podman runtime manager.
|
||||
impl Podman {
|
||||
/// new creates a new podman runtime manager.
|
||||
#[instrument(skip_all)]
|
||||
pub fn new(config: dfinit::Podman, proxy_config: dfinit::Proxy) -> Self {
|
||||
Self {
|
||||
config,
|
||||
proxy_config,
|
||||
}
|
||||
}
|
||||
|
||||
/// run runs the podman runtime to initialize
|
||||
/// runtime environment for the dfdaemon.
|
||||
#[instrument(skip_all)]
|
||||
pub async fn run(&self) -> Result<()> {
|
||||
let mut registries_config_table = toml_edit::DocumentMut::new();
|
||||
registries_config_table.set_implicit(true);
|
||||
|
||||
// Add unqualified-search-registries to registries config.
|
||||
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),
|
||||
);
|
||||
|
||||
// Parse proxy address to get host and port.
|
||||
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);
|
||||
|
||||
// Add registries to the registries config.
|
||||
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::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_podman_config() {
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
let podman_config_file = NamedTempFile::new().unwrap();
|
||||
let podman = Podman::new(
|
||||
dfinit::Podman {
|
||||
config_path: podman_config_file.path().to_path_buf(),
|
||||
registries: vec![dfinit::PodmanRegistry {
|
||||
prefix: "registry.example.com".into(),
|
||||
location: "registry.example.com".into(),
|
||||
}],
|
||||
unqualified_search_registries: vec!["registry.example.com".into()],
|
||||
},
|
||||
dfinit::Proxy {
|
||||
addr: "http://127.0.0.1:5000".into(),
|
||||
},
|
||||
);
|
||||
let result = podman.run().await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
|
||||
// get the contents of the file
|
||||
let contents = fs::read_to_string(podman_config_file.path().to_path_buf())
|
||||
.await
|
||||
.unwrap();
|
||||
let expected_contents = 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"
|
||||
"#;
|
||||
// assert that the contents of the file are as expected
|
||||
assert_eq!(contents, expected_contents);
|
||||
|
||||
// clean up
|
||||
fs::remove_file(podman_config_file.path().to_path_buf())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue