mirror of
https://github.com/faas-rs/faasd-in-rust.git
synced 2025-06-19 04:26:33 +00:00
Feat(image) 移动image成为单独的逻辑 (#61)
* 更改cni的位置 * 提取关于Image的逻辑 * 更改全局ImageMap的名字 * runtime_config获取部分改为expect * 修改save_img_config的位置 * 完善一下错误处理 * 遗漏的错误处理 * 增加deploy处的错误处理 * fmt
This commit is contained in:
411
crates/service/src/image_manager.rs
Normal file
411
crates/service/src/image_manager.rs
Normal file
@ -0,0 +1,411 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use containerd_client::{
|
||||
Client,
|
||||
services::v1::{GetImageRequest, ReadContentRequest, TransferOptions, TransferRequest},
|
||||
to_any,
|
||||
tonic::Request,
|
||||
types::{
|
||||
Platform,
|
||||
transfer::{ImageStore, OciRegistry, UnpackConfiguration},
|
||||
},
|
||||
with_namespace,
|
||||
};
|
||||
use oci_spec::image::{Arch, ImageConfiguration, ImageIndex, ImageManifest, MediaType, Os};
|
||||
|
||||
use crate::spec::DEFAULT_NAMESPACE;
|
||||
|
||||
type ImagesMap = Arc<RwLock<HashMap<String, ImageConfiguration>>>;
|
||||
lazy_static::lazy_static! {
|
||||
static ref GLOBAL_IMAGE_MAP: ImagesMap = Arc::new(RwLock::new(HashMap::new()));
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImageRuntimeConfig {
|
||||
pub env: Vec<String>,
|
||||
pub args: Vec<String>,
|
||||
pub ports: Vec<String>,
|
||||
}
|
||||
|
||||
impl ImageRuntimeConfig {
|
||||
pub fn new(env: Vec<String>, args: Vec<String>, ports: Vec<String>) -> Self {
|
||||
ImageRuntimeConfig { env, args, ports }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ImageManager {
|
||||
fn drop(&mut self) {
|
||||
let mut map = GLOBAL_IMAGE_MAP.write().unwrap();
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ImageError {
|
||||
ImageNotFound(String),
|
||||
ImagePullFailed(String),
|
||||
ImageConfigurationNotFound(String),
|
||||
ReadContentFailed(String),
|
||||
UnexpectedMediaType,
|
||||
DeserializationFailed(String),
|
||||
#[allow(dead_code)]
|
||||
OtherError,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ImageError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ImageError::ImageNotFound(msg) => write!(f, "Image not found: {}", msg),
|
||||
ImageError::ImagePullFailed(msg) => write!(f, "Image pull failed: {}", msg),
|
||||
ImageError::ImageConfigurationNotFound(msg) => {
|
||||
write!(f, "Image configuration not found: {}", msg)
|
||||
}
|
||||
ImageError::ReadContentFailed(msg) => write!(f, "Read content failed: {}", msg),
|
||||
ImageError::UnexpectedMediaType => {
|
||||
write!(f, "Unexpected media type")
|
||||
}
|
||||
ImageError::DeserializationFailed(msg) => {
|
||||
write!(f, "Deserialization failed: {}", msg)
|
||||
}
|
||||
ImageError::OtherError => write!(f, "Other error happened"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ImageError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ImageManager;
|
||||
|
||||
impl ImageManager {
|
||||
pub async fn prepare_image(
|
||||
client: &Client,
|
||||
image_name: &str,
|
||||
ns: &str,
|
||||
always_pull: bool,
|
||||
) -> Result<(), ImageError> {
|
||||
if always_pull {
|
||||
Self::pull_image(client, image_name, ns).await?;
|
||||
} else {
|
||||
let namespace = check_namespace(ns);
|
||||
let namespace = namespace.as_str();
|
||||
let mut c = client.images();
|
||||
let req = GetImageRequest {
|
||||
name: image_name.to_string(),
|
||||
};
|
||||
|
||||
let resp = match c.get(with_namespace!(req, namespace)).await {
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(e) => {
|
||||
return Err(ImageError::ImageNotFound(format!(
|
||||
"Failed to get image {}: {}",
|
||||
image_name, e
|
||||
)));
|
||||
}
|
||||
};
|
||||
if resp.image.is_none() {
|
||||
Self::pull_image(client, image_name, ns).await?;
|
||||
}
|
||||
}
|
||||
Self::save_img_config(client, image_name, ns).await
|
||||
}
|
||||
|
||||
pub async fn pull_image(client: &Client, image_name: &str, ns: &str) -> Result<(), ImageError> {
|
||||
let ns = check_namespace(ns);
|
||||
let namespace = ns.as_str();
|
||||
|
||||
let mut c: containerd_client::services::v1::transfer_client::TransferClient<
|
||||
tonic::transport::Channel,
|
||||
> = client.transfer();
|
||||
let source = OciRegistry {
|
||||
reference: image_name.to_string(),
|
||||
resolver: Default::default(),
|
||||
};
|
||||
|
||||
// 这里先写死linux amd64
|
||||
let platform = Platform {
|
||||
os: "linux".to_string(),
|
||||
architecture: "amd64".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let dest = ImageStore {
|
||||
name: image_name.to_string(),
|
||||
platforms: vec![platform.clone()],
|
||||
unpacks: vec![UnpackConfiguration {
|
||||
platform: Some(platform),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let anys = to_any(&source);
|
||||
let anyd = to_any(&dest);
|
||||
|
||||
let req = TransferRequest {
|
||||
source: Some(anys),
|
||||
destination: Some(anyd),
|
||||
options: Some(TransferOptions {
|
||||
..Default::default()
|
||||
}),
|
||||
};
|
||||
|
||||
if let Err(e) = c.transfer(with_namespace!(req, namespace)).await {
|
||||
return Err(ImageError::ImagePullFailed(format!(
|
||||
"Failed to pull image {}: {}",
|
||||
image_name, e
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
// Self::save_img_config(client, image_name, ns.as_str()).await
|
||||
}
|
||||
|
||||
pub async fn save_img_config(
|
||||
client: &Client,
|
||||
img_name: &str,
|
||||
ns: &str,
|
||||
) -> Result<(), ImageError> {
|
||||
let mut c = client.images();
|
||||
|
||||
let req = GetImageRequest {
|
||||
name: img_name.to_string(),
|
||||
};
|
||||
let resp = match c.get(with_namespace!(req, ns)).await {
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(e) => {
|
||||
return Err(ImageError::ImageNotFound(format!(
|
||||
"Failed to get image {}: {}",
|
||||
img_name, e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let img_dscr = resp.image.unwrap().target.unwrap();
|
||||
let media_type = MediaType::from(img_dscr.media_type.as_str());
|
||||
|
||||
let req = ReadContentRequest {
|
||||
digest: img_dscr.digest,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut c = client.content();
|
||||
|
||||
let mut inner = match c.read(with_namespace!(req, ns)).await {
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(e) => {
|
||||
return Err(ImageError::ReadContentFailed(format!(
|
||||
"Failed to read content of image {}: {}",
|
||||
img_name, e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let resp = match inner.message().await {
|
||||
Ok(response) => response.unwrap().data,
|
||||
Err(e) => {
|
||||
return Err(ImageError::ReadContentFailed(format!(
|
||||
"Failed to get the inner content of image {}: {}",
|
||||
img_name, e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
drop(c);
|
||||
|
||||
let img_config = match media_type {
|
||||
MediaType::ImageIndex => Self::handle_index(client, &resp, ns).await.unwrap(),
|
||||
MediaType::ImageManifest => Self::handle_manifest(client, &resp, ns).await.unwrap(),
|
||||
MediaType::Other(media_type) => match media_type.as_str() {
|
||||
"application/vnd.docker.distribution.manifest.list.v2+json" => {
|
||||
Self::handle_index(client, &resp, ns).await.unwrap()
|
||||
}
|
||||
"application/vnd.docker.distribution.manifest.v2+json" => {
|
||||
Self::handle_manifest(client, &resp, ns).await.unwrap()
|
||||
}
|
||||
_ => {
|
||||
return Err(ImageError::UnexpectedMediaType);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(ImageError::UnexpectedMediaType);
|
||||
}
|
||||
};
|
||||
if img_config.is_none() {
|
||||
return Err(ImageError::ImageConfigurationNotFound(format!(
|
||||
"save_img_config: Image configuration not found for image {}",
|
||||
img_name
|
||||
)));
|
||||
}
|
||||
let img_config = img_config.unwrap();
|
||||
Self::insert_image_config(img_name, img_config)
|
||||
}
|
||||
|
||||
async fn handle_index(
|
||||
client: &Client,
|
||||
data: &[u8],
|
||||
ns: &str,
|
||||
) -> Result<Option<ImageConfiguration>, ImageError> {
|
||||
let image_index: ImageIndex = ::serde_json::from_slice(data).map_err(|e| {
|
||||
ImageError::DeserializationFailed(format!("Failed to parse JSON: {}", e))
|
||||
})?;
|
||||
let img_manifest_dscr = image_index
|
||||
.manifests()
|
||||
.iter()
|
||||
.find(|manifest_entry| match manifest_entry.platform() {
|
||||
Some(p) => {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
matches!(p.architecture(), &Arch::Amd64) && matches!(p.os(), &Os::Linux)
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
matches!(p.architecture(), &Arch::ARM64) && matches!(p.os(), Os::Linux)
|
||||
//&& matches!(p.variant().as_ref().map(|s| s.as_str()), Some("v8"))
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let req = ReadContentRequest {
|
||||
digest: img_manifest_dscr.digest().to_owned(),
|
||||
offset: 0,
|
||||
size: 0,
|
||||
};
|
||||
|
||||
let mut c = client.content();
|
||||
let mut inner = match c.read(with_namespace!(req, ns)).await {
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(e) => {
|
||||
return Err(ImageError::ReadContentFailed(format!(
|
||||
"Failed to handler index : {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let resp = match inner.message().await {
|
||||
Ok(response) => response.unwrap().data,
|
||||
Err(e) => {
|
||||
return Err(ImageError::ReadContentFailed(format!(
|
||||
"Failed to handle index inner : {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
};
|
||||
drop(c);
|
||||
|
||||
Self::handle_manifest(client, &resp, ns).await
|
||||
}
|
||||
|
||||
async fn handle_manifest(
|
||||
client: &Client,
|
||||
data: &[u8],
|
||||
ns: &str,
|
||||
) -> Result<Option<ImageConfiguration>, ImageError> {
|
||||
let img_manifest: ImageManifest = match ::serde_json::from_slice(data) {
|
||||
Ok(manifest) => manifest,
|
||||
Err(e) => {
|
||||
return Err(ImageError::DeserializationFailed(format!(
|
||||
"Failed to deserialize image manifest: {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
};
|
||||
let img_manifest_dscr = img_manifest.config();
|
||||
|
||||
let req = ReadContentRequest {
|
||||
digest: img_manifest_dscr.digest().to_owned(),
|
||||
offset: 0,
|
||||
size: 0,
|
||||
};
|
||||
let mut c = client.content();
|
||||
|
||||
let mut inner = match c.read(with_namespace!(req, ns)).await {
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(e) => {
|
||||
return Err(ImageError::ReadContentFailed(format!(
|
||||
"Failed to handler index : {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let resp = match inner.message().await {
|
||||
Ok(response) => response.unwrap().data,
|
||||
Err(e) => {
|
||||
return Err(ImageError::ReadContentFailed(format!(
|
||||
"Failed to handle index inner : {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(::serde_json::from_slice(&resp).unwrap())
|
||||
}
|
||||
|
||||
fn insert_image_config(image_name: &str, config: ImageConfiguration) -> Result<(), ImageError> {
|
||||
let mut map = GLOBAL_IMAGE_MAP.write().unwrap();
|
||||
map.insert(image_name.to_string(), config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_image_config(image_name: &str) -> Result<ImageConfiguration, ImageError> {
|
||||
let map = GLOBAL_IMAGE_MAP.read().unwrap();
|
||||
if let Some(config) = map.get(image_name) {
|
||||
Ok(config.clone())
|
||||
} else {
|
||||
Err(ImageError::ImageConfigurationNotFound(format!(
|
||||
"get_image_config: Image configuration not found for image {}",
|
||||
image_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_runtime_config(image_name: &str) -> Result<ImageRuntimeConfig, ImageError> {
|
||||
let map = GLOBAL_IMAGE_MAP.read().unwrap();
|
||||
if let Some(config) = map.get(image_name) {
|
||||
if let Some(config) = config.config() {
|
||||
let env = config
|
||||
.env()
|
||||
.clone()
|
||||
.expect("Failed to get environment variables");
|
||||
let args = config
|
||||
.cmd()
|
||||
.clone()
|
||||
.expect("Failed to get command arguments");
|
||||
let ports = config
|
||||
.exposed_ports()
|
||||
.clone()
|
||||
.expect("Failed to get exposed ports");
|
||||
Ok(ImageRuntimeConfig::new(env, args, ports))
|
||||
} else {
|
||||
Err(ImageError::ImageConfigurationNotFound(format!(
|
||||
"Image configuration is empty for image {}",
|
||||
image_name
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(ImageError::ImageConfigurationNotFound(format!(
|
||||
"get_runtime_config: Image configuration not found for image {}",
|
||||
image_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// 不用这个也能拉取镜像?
|
||||
pub fn get_resolver() {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn check_namespace(ns: &str) -> String {
|
||||
match ns {
|
||||
"" => DEFAULT_NAMESPACE.to_string(),
|
||||
_ => ns.to_string(),
|
||||
}
|
||||
}
|
@ -1,27 +1,21 @@
|
||||
pub mod image_manager;
|
||||
pub mod spec;
|
||||
pub mod systemd;
|
||||
|
||||
use cni::cni_network::init_net_work;
|
||||
use containerd_client::{
|
||||
Client,
|
||||
services::v1::{
|
||||
Container, CreateContainerRequest, CreateTaskRequest, DeleteContainerRequest,
|
||||
DeleteTaskRequest, GetImageRequest, KillRequest, ListContainersRequest,
|
||||
ListNamespacesRequest, ListTasksRequest, ReadContentRequest, StartRequest, TransferOptions,
|
||||
TransferRequest, WaitRequest,
|
||||
DeleteTaskRequest, KillRequest, ListContainersRequest, ListNamespacesRequest,
|
||||
ListTasksRequest, StartRequest, WaitRequest,
|
||||
container::Runtime,
|
||||
snapshots::{MountsRequest, PrepareSnapshotRequest},
|
||||
},
|
||||
to_any,
|
||||
tonic::Request,
|
||||
types::{
|
||||
Mount, Platform,
|
||||
transfer::{ImageStore, OciRegistry, UnpackConfiguration},
|
||||
v1::Process,
|
||||
},
|
||||
types::v1::Process,
|
||||
with_namespace,
|
||||
};
|
||||
use oci_spec::image::{Arch, ImageConfiguration, ImageIndex, ImageManifest, MediaType, Os};
|
||||
use image_manager::ImageManager;
|
||||
use prost_types::Any;
|
||||
use sha2::{Digest, Sha256};
|
||||
use spec::{DEFAULT_NAMESPACE, generate_spec};
|
||||
@ -30,7 +24,6 @@ use std::{
|
||||
fs,
|
||||
sync::{Arc, RwLock},
|
||||
time::Duration,
|
||||
vec,
|
||||
};
|
||||
use tokio::time::timeout;
|
||||
|
||||
@ -45,7 +38,7 @@ lazy_static::lazy_static! {
|
||||
type Err = Box<dyn std::error::Error>;
|
||||
|
||||
pub struct Service {
|
||||
client: Arc<Client>,
|
||||
pub client: Arc<Client>,
|
||||
netns_map: NetnsMap,
|
||||
}
|
||||
|
||||
@ -70,13 +63,12 @@ impl Service {
|
||||
|
||||
pub async fn get_ip(&self, cid: &str) -> Option<String> {
|
||||
let map = self.netns_map.read().unwrap();
|
||||
map.get(cid).map(|net_conf| net_conf.ip.clone())
|
||||
map.get(cid).map(|net_conf| net_conf.get_ip())
|
||||
}
|
||||
|
||||
pub async fn get_address(&self, cid: &str) -> Option<String> {
|
||||
let map = self.netns_map.read().unwrap();
|
||||
map.get(cid)
|
||||
.map(|net_conf| format!("{}:{}", net_conf.ip, net_conf.ports[0]))
|
||||
map.get(cid).map(|net_conf| net_conf.get_address())
|
||||
}
|
||||
|
||||
pub async fn remove_netns_ip(&self, cid: &str) {
|
||||
@ -84,40 +76,33 @@ impl Service {
|
||||
map.remove(cid);
|
||||
}
|
||||
|
||||
async fn prepare_snapshot(
|
||||
&self,
|
||||
cid: &str,
|
||||
ns: &str,
|
||||
img_name: &str,
|
||||
) -> Result<Vec<Mount>, Err> {
|
||||
let parent_snapshot = self.get_parent_snapshot(img_name, ns).await?;
|
||||
async fn prepare_snapshot(&self, cid: &str, ns: &str, img_name: &str) -> Result<(), Err> {
|
||||
let parent_snapshot = self.get_parent_snapshot(img_name).await?;
|
||||
let req = PrepareSnapshotRequest {
|
||||
snapshotter: "overlayfs".to_string(),
|
||||
key: cid.to_string(),
|
||||
parent: parent_snapshot,
|
||||
..Default::default()
|
||||
};
|
||||
let resp = self
|
||||
let _resp = self
|
||||
.client
|
||||
.snapshots()
|
||||
.prepare(with_namespace!(req, ns))
|
||||
.await?
|
||||
.into_inner()
|
||||
.mounts;
|
||||
.await?;
|
||||
|
||||
Ok(resp)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_container(&self, image_name: &str, cid: &str, ns: &str) -> Result<(), Err> {
|
||||
let namespace = match ns {
|
||||
"" => spec::DEFAULT_NAMESPACE,
|
||||
_ => ns,
|
||||
};
|
||||
let namespace = self.check_namespace(ns);
|
||||
let namespace = namespace.as_str();
|
||||
|
||||
self.prepare_snapshot(cid, ns, image_name).await?;
|
||||
let config = ImageManager::get_runtime_config(image_name).unwrap();
|
||||
|
||||
let _mount = self.prepare_snapshot(cid, ns, image_name).await?;
|
||||
let config = self.get_runtime_config(image_name, ns).await?;
|
||||
let env = config.env;
|
||||
let args = config.args;
|
||||
|
||||
let spec_path = generate_spec(cid, ns, args, env).unwrap();
|
||||
let spec = fs::read_to_string(spec_path).unwrap();
|
||||
|
||||
@ -144,14 +129,11 @@ impl Service {
|
||||
container: Some(container),
|
||||
};
|
||||
|
||||
// let req = with_namespace!(req, namespace);
|
||||
|
||||
let _resp = containers_client
|
||||
.create(with_namespace!(req, namespace))
|
||||
.await
|
||||
.expect("Failed to create container");
|
||||
|
||||
// println!("Container: {:?} created", cid);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -206,6 +188,7 @@ impl Service {
|
||||
.delete(with_namespace!(delete_request, namespace))
|
||||
.await
|
||||
.expect("Failed to delete container");
|
||||
//todo 这里删除cni?
|
||||
self.remove_netns_ip(cid).await;
|
||||
|
||||
println!("Container: {:?} deleted", cc);
|
||||
@ -216,49 +199,44 @@ impl Service {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_and_start_task(&self, cid: &str, ns: &str) -> Result<(), Err> {
|
||||
// let tmp = std::env::temp_dir().join("containerd-client-test");
|
||||
// println!("Temp dir: {:?}", tmp);
|
||||
// fs::create_dir_all(&tmp).expect("Failed to create temp directory");
|
||||
// let stdin = tmp.join("stdin");
|
||||
// let stdout = tmp.join("stdout");
|
||||
// let stderr = tmp.join("stderr");
|
||||
// File::create(&stdin).expect("Failed to create stdin");
|
||||
// File::create(&stdout).expect("Failed to create stdout");
|
||||
// File::create(&stderr).expect("Failed to create stderr");
|
||||
|
||||
let namespace = match ns {
|
||||
"" => spec::DEFAULT_NAMESPACE,
|
||||
_ => ns,
|
||||
};
|
||||
self.create_task(cid, namespace).await?;
|
||||
pub async fn create_and_start_task(
|
||||
&self,
|
||||
cid: &str,
|
||||
ns: &str,
|
||||
img_name: &str,
|
||||
) -> Result<(), Err> {
|
||||
let namespace = self.check_namespace(ns);
|
||||
let namespace = namespace.as_str();
|
||||
self.create_task(cid, namespace, img_name).await?;
|
||||
self.start_task(cid, namespace).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 返回任务的pid
|
||||
async fn create_task(&self, cid: &str, ns: &str) -> Result<u32, Err> {
|
||||
async fn create_task(&self, cid: &str, ns: &str, img_name: &str) -> Result<u32, Err> {
|
||||
let mut sc = self.client.snapshots();
|
||||
let req = MountsRequest {
|
||||
snapshotter: "overlayfs".to_string(),
|
||||
key: cid.to_string(),
|
||||
};
|
||||
|
||||
let mounts = sc
|
||||
.mounts(with_namespace!(req, ns))
|
||||
.await?
|
||||
.into_inner()
|
||||
.mounts;
|
||||
|
||||
println!("mounts ok");
|
||||
drop(sc);
|
||||
println!("drop sc ok");
|
||||
let _ = init_net_work();
|
||||
let _ = cni::init_net_work();
|
||||
println!("init_net_work ok");
|
||||
let (ip, path) = cni::cni_network::create_cni_network(cid.to_string(), ns.to_string())?;
|
||||
let ports = self.get_runtime_config(cid, ns).await?.ports;
|
||||
let (ip, path) = cni::create_cni_network(cid.to_string(), ns.to_string())?;
|
||||
let ports = ImageManager::get_runtime_config(img_name).unwrap().ports;
|
||||
let network_config = NetworkConfig::new(path, ip, ports);
|
||||
println!("create_cni_network ok");
|
||||
self.save_network_config(cid, network_config).await;
|
||||
println!("save_netns_ip ok");
|
||||
self.save_network_config(cid, network_config.clone()).await;
|
||||
println!("save_netns_ip ok, netconfig: {:?}", network_config);
|
||||
let mut tc = self.client.tasks();
|
||||
let req = CreateTaskRequest {
|
||||
container_id: cid.to_string(),
|
||||
@ -442,245 +420,8 @@ impl Service {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn prepare_image(
|
||||
&self,
|
||||
image_name: &str,
|
||||
ns: &str,
|
||||
always_pull: bool,
|
||||
) -> Result<(), Err> {
|
||||
if always_pull {
|
||||
self.pull_image(image_name, ns).await?;
|
||||
} else {
|
||||
let namespace = self.check_namespace(ns);
|
||||
let namespace = namespace.as_str();
|
||||
let mut c = self.client.images();
|
||||
let req = GetImageRequest {
|
||||
name: image_name.to_string(),
|
||||
};
|
||||
let resp = c
|
||||
.get(with_namespace!(req, namespace))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!(
|
||||
"Failed to get the config of {} in namespace {}: {}",
|
||||
image_name, namespace, e
|
||||
);
|
||||
e
|
||||
})
|
||||
.ok()
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
if resp.image.is_none() {
|
||||
self.pull_image(image_name, ns).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn pull_image(&self, image_name: &str, ns: &str) -> Result<(), Err> {
|
||||
let namespace = self.check_namespace(ns);
|
||||
let namespace = namespace.as_str();
|
||||
|
||||
let mut c = self.client.transfer();
|
||||
let source = OciRegistry {
|
||||
reference: image_name.to_string(),
|
||||
resolver: Default::default(),
|
||||
};
|
||||
// 这里先写死linux amd64
|
||||
let platform = Platform {
|
||||
os: "linux".to_string(),
|
||||
architecture: "amd64".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
let dest = ImageStore {
|
||||
name: image_name.to_string(),
|
||||
platforms: vec![platform.clone()],
|
||||
unpacks: vec![UnpackConfiguration {
|
||||
platform: Some(platform),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let anys = to_any(&source);
|
||||
let anyd = to_any(&dest);
|
||||
|
||||
let req = TransferRequest {
|
||||
source: Some(anys),
|
||||
destination: Some(anyd),
|
||||
options: Some(TransferOptions {
|
||||
..Default::default()
|
||||
}),
|
||||
};
|
||||
c.transfer(with_namespace!(req, namespace))
|
||||
.await
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Unable to transfer image {} to namespace {}",
|
||||
image_name, namespace
|
||||
)
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 不用这个也能拉取镜像?
|
||||
pub fn get_resolver(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn handle_index(&self, data: &[u8], ns: &str) -> Option<ImageConfiguration> {
|
||||
let image_index: ImageIndex = ::serde_json::from_slice(data).unwrap();
|
||||
let img_manifest_dscr = image_index
|
||||
.manifests()
|
||||
.iter()
|
||||
.find(|manifest_entry| match manifest_entry.platform() {
|
||||
Some(p) => {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
matches!(p.architecture(), &Arch::Amd64) && matches!(p.os(), &Os::Linux)
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
matches!(p.architecture(), &Arch::ARM64) && matches!(p.os(), Os::Linux)
|
||||
//&& matches!(p.variant().as_ref().map(|s| s.as_str()), Some("v8"))
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let req = ReadContentRequest {
|
||||
digest: img_manifest_dscr.digest().to_owned(),
|
||||
offset: 0,
|
||||
size: 0,
|
||||
};
|
||||
|
||||
let mut c = self.client.content();
|
||||
let resp = c
|
||||
.read(with_namespace!(req, ns))
|
||||
.await
|
||||
.expect("Failed to read content")
|
||||
.into_inner()
|
||||
.message()
|
||||
.await
|
||||
.expect("Failed to read content message")
|
||||
.unwrap()
|
||||
.data;
|
||||
|
||||
self.handle_manifest(&resp, ns).await
|
||||
}
|
||||
|
||||
async fn handle_manifest(&self, data: &[u8], ns: &str) -> Option<ImageConfiguration> {
|
||||
let img_manifest: ImageManifest = ::serde_json::from_slice(data).unwrap();
|
||||
let img_manifest_dscr = img_manifest.config();
|
||||
|
||||
let req = ReadContentRequest {
|
||||
digest: img_manifest_dscr.digest().to_owned(),
|
||||
offset: 0,
|
||||
size: 0,
|
||||
};
|
||||
let mut c = self.client.content();
|
||||
|
||||
let resp = c
|
||||
.read(with_namespace!(req, ns))
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.message()
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.data;
|
||||
|
||||
::serde_json::from_slice(&resp).unwrap()
|
||||
}
|
||||
|
||||
pub async fn get_img_config(&self, img_name: &str, ns: &str) -> Option<ImageConfiguration> {
|
||||
let mut c = self.client.images();
|
||||
|
||||
let req = GetImageRequest {
|
||||
name: img_name.to_string(),
|
||||
};
|
||||
let resp = c
|
||||
.get(with_namespace!(req, ns))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!(
|
||||
"Failed to get the config of {} in namespace {}: {}",
|
||||
img_name, ns, e
|
||||
);
|
||||
e
|
||||
})
|
||||
.ok()?
|
||||
.into_inner();
|
||||
|
||||
let img_dscr = resp.image?.target?;
|
||||
let media_type = MediaType::from(img_dscr.media_type.as_str());
|
||||
|
||||
let req = ReadContentRequest {
|
||||
digest: img_dscr.digest,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut c = self.client.content();
|
||||
|
||||
let resp = c
|
||||
.read(with_namespace!(req, ns))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!(
|
||||
"Failed to read content for {} in namespace {}: {}",
|
||||
img_name, ns, e
|
||||
);
|
||||
e
|
||||
})
|
||||
.ok()?
|
||||
.into_inner()
|
||||
.message()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!(
|
||||
"Failed to read message for {} in namespace {}: {}",
|
||||
img_name, ns, e
|
||||
);
|
||||
e
|
||||
})
|
||||
.ok()?
|
||||
.ok_or_else(|| {
|
||||
eprintln!("No data found for {} in namespace {}", img_name, ns);
|
||||
std::io::Error::new(std::io::ErrorKind::NotFound, "No data found")
|
||||
})
|
||||
.ok()?
|
||||
.data;
|
||||
|
||||
let img_config = match media_type {
|
||||
MediaType::ImageIndex => self.handle_index(&resp, ns).await.unwrap(),
|
||||
MediaType::ImageManifest => self.handle_manifest(&resp, ns).await.unwrap(),
|
||||
MediaType::Other(media_type) => match media_type.as_str() {
|
||||
"application/vnd.docker.distribution.manifest.list.v2+json" => {
|
||||
self.handle_index(&resp, ns).await.unwrap()
|
||||
}
|
||||
"application/vnd.docker.distribution.manifest.v2+json" => {
|
||||
self.handle_manifest(&resp, ns).await.unwrap()
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Unexpected media type '{}'", media_type);
|
||||
return None;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
eprintln!("Unexpected media type '{}'", media_type);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(img_config)
|
||||
}
|
||||
|
||||
async fn get_parent_snapshot(&self, img_name: &str, ns: &str) -> Result<String, Err> {
|
||||
let img_config = match self.get_img_config(img_name, ns).await {
|
||||
Some(config) => config,
|
||||
None => return Err("Failed to get image configuration".into()),
|
||||
};
|
||||
async fn get_parent_snapshot(&self, img_name: &str) -> Result<String, Err> {
|
||||
let img_config = image_manager::ImageManager::get_image_config(img_name)?;
|
||||
|
||||
let mut iter = img_config.rootfs().diff_ids().iter();
|
||||
let mut ret = iter
|
||||
@ -699,21 +440,6 @@ impl Service {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn get_runtime_config(&self, name: &str, ns: &str) -> Result<RunTimeConfig, Err> {
|
||||
let img_config = self.get_img_config(name, ns).await.unwrap();
|
||||
if let Some(config) = img_config.config() {
|
||||
let env = config.env().as_ref().map_or_else(Vec::new, |v| v.clone());
|
||||
let args = config.cmd().as_ref().map_or_else(Vec::new, |v| v.clone());
|
||||
let ports = config
|
||||
.exposed_ports()
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |v| v.clone());
|
||||
Ok(RunTimeConfig::new(env, args, ports))
|
||||
} else {
|
||||
Err("No config found".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_namespace(&self, ns: &str) -> String {
|
||||
match ns {
|
||||
"" => DEFAULT_NAMESPACE.to_string(),
|
||||
@ -744,31 +470,32 @@ impl Service {
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
//容器是容器,要先启动,然后才能运行任务
|
||||
//要想删除一个正在运行的Task,必须先kill掉这个task,然后才能删除。
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RunTimeConfig {
|
||||
pub env: Vec<String>,
|
||||
pub args: Vec<String>,
|
||||
pub ports: Vec<String>,
|
||||
}
|
||||
|
||||
impl RunTimeConfig {
|
||||
pub fn new(env: Vec<String>, args: Vec<String>, ports: Vec<String>) -> Self {
|
||||
RunTimeConfig { env, args, ports }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkConfig {
|
||||
pub netns: String,
|
||||
pub ip: String,
|
||||
pub ports: Vec<String>,
|
||||
netns: String,
|
||||
ip: String,
|
||||
ports: Vec<String>,
|
||||
}
|
||||
|
||||
impl NetworkConfig {
|
||||
pub fn new(netns: String, ip: String, ports: Vec<String>) -> Self {
|
||||
NetworkConfig { netns, ip, ports }
|
||||
}
|
||||
|
||||
pub fn get_netns(&self) -> String {
|
||||
self.netns.clone()
|
||||
}
|
||||
|
||||
pub fn get_ip(&self) -> String {
|
||||
self.ip.clone()
|
||||
}
|
||||
|
||||
pub fn get_address(&self) -> String {
|
||||
format!(
|
||||
"{}:{}",
|
||||
self.ip.split('/').next().unwrap_or(""),
|
||||
self.ports[0].split('/').next().unwrap_or("")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user