mirror of
https://github.com/faas-rs/faasd-in-rust.git
synced 2025-06-08 07:55:04 +00:00
feat: 添加namespace相关api (#108)
This commit is contained in:
parent
308e9bcc5d
commit
ce81aa84c5
@ -2,6 +2,7 @@ pub mod cni;
|
||||
pub mod container;
|
||||
pub mod error;
|
||||
pub mod function;
|
||||
pub mod namespace;
|
||||
pub mod oci_image;
|
||||
pub mod snapshot;
|
||||
pub mod spec;
|
||||
|
144
crates/faas-containerd/src/impls/namespace.rs
Normal file
144
crates/faas-containerd/src/impls/namespace.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use containerd_client::services::v1::{
|
||||
CreateNamespaceRequest, DeleteNamespaceRequest, ListNamespacesRequest, Namespace,
|
||||
UpdateNamespaceRequest,
|
||||
};
|
||||
use derive_more::Display;
|
||||
|
||||
use super::ContainerdService;
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum NamespaceServiceError {
|
||||
AlreadyExists,
|
||||
NotFound,
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
impl From<tonic::Status> for NamespaceServiceError {
|
||||
fn from(status: tonic::Status) -> Self {
|
||||
use tonic::Code::*;
|
||||
match status.code() {
|
||||
NotFound => NamespaceServiceError::NotFound,
|
||||
AlreadyExists => NamespaceServiceError::AlreadyExists,
|
||||
_ => NamespaceServiceError::Internal(status.message().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContainerdService {
|
||||
// 创建命名空间
|
||||
pub async fn create_namespace(
|
||||
&self,
|
||||
namespace: &str,
|
||||
labels: HashMap<String, String>,
|
||||
) -> Result<(), NamespaceServiceError> {
|
||||
let exist = self.namespace_exist(namespace).await?;
|
||||
if exist.is_some() {
|
||||
log::info!("Namespace {} already exists", namespace);
|
||||
return Err(NamespaceServiceError::AlreadyExists);
|
||||
}
|
||||
|
||||
let mut c = self.client.namespaces();
|
||||
let ns_name = namespace;
|
||||
|
||||
let namespace = Namespace {
|
||||
name: namespace.to_string(),
|
||||
labels,
|
||||
};
|
||||
let req = CreateNamespaceRequest {
|
||||
namespace: Some(namespace),
|
||||
};
|
||||
c.create(req).await.map_err(|e| {
|
||||
log::error!("Failed to create namespace: {}", e);
|
||||
NamespaceServiceError::Internal(e.to_string())
|
||||
})?;
|
||||
log::info!("Namespace {} created", ns_name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 删除命名空间
|
||||
pub async fn delete_namespace(&self, namespace: &str) -> Result<(), NamespaceServiceError> {
|
||||
let exist = self.namespace_exist(namespace).await?;
|
||||
if exist.is_none() {
|
||||
log::info!("Namespace {} not found", namespace);
|
||||
return Err(NamespaceServiceError::NotFound);
|
||||
}
|
||||
let mut c = self.client.namespaces();
|
||||
let ns_name = namespace;
|
||||
let req = DeleteNamespaceRequest {
|
||||
name: namespace.to_string(),
|
||||
};
|
||||
c.delete(req).await.map_err(|e| {
|
||||
log::error!("Failed to delete namespace: {}", e);
|
||||
NamespaceServiceError::Internal(e.to_string())
|
||||
})?;
|
||||
log::info!("Namespace {} deleted", ns_name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 判断命名空间是否存在
|
||||
pub async fn namespace_exist(
|
||||
&self,
|
||||
namespace: &str,
|
||||
) -> Result<Option<Namespace>, NamespaceServiceError> {
|
||||
let mut c = self.client.namespaces();
|
||||
let req = ListNamespacesRequest {
|
||||
..Default::default()
|
||||
};
|
||||
let ns_list_resp = c.list(req).await.map_err(|e| {
|
||||
log::error!("Failed to list namespaces: {}", e);
|
||||
NamespaceServiceError::Internal(e.to_string())
|
||||
})?;
|
||||
let ns_list = ns_list_resp.into_inner().namespaces;
|
||||
for ns in ns_list {
|
||||
if ns.name == namespace {
|
||||
return Ok(Some(ns));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// 获取命名空间列表
|
||||
pub async fn list_namespace(&self) -> Result<Vec<Namespace>, NamespaceServiceError> {
|
||||
let mut c = self.client.namespaces();
|
||||
let req = ListNamespacesRequest {
|
||||
..Default::default()
|
||||
};
|
||||
let ns_list_resp = c.list(req).await.map_err(|e| {
|
||||
log::error!("Failed to list namespaces: {}", e);
|
||||
NamespaceServiceError::Internal(e.to_string())
|
||||
})?;
|
||||
let ns_list = ns_list_resp.into_inner().namespaces;
|
||||
Ok(ns_list)
|
||||
}
|
||||
|
||||
// 更新命名空间信息
|
||||
pub async fn update_namespace(
|
||||
&self,
|
||||
namespace: &str,
|
||||
labels: HashMap<String, String>,
|
||||
) -> Result<(), NamespaceServiceError> {
|
||||
let exist = self.namespace_exist(namespace).await?;
|
||||
if exist.is_none() {
|
||||
log::info!("Namespace {} not found", namespace);
|
||||
return Err(NamespaceServiceError::NotFound);
|
||||
}
|
||||
let ns_name = namespace;
|
||||
let namespace = Namespace {
|
||||
name: namespace.to_string(),
|
||||
labels,
|
||||
};
|
||||
let mut c = self.client.namespaces();
|
||||
let req = UpdateNamespaceRequest {
|
||||
namespace: Some(namespace),
|
||||
..Default::default()
|
||||
};
|
||||
c.update(req).await.map_err(|e| {
|
||||
log::error!("Failed to update namespace: {}", e);
|
||||
NamespaceServiceError::Internal(e.to_string())
|
||||
})?;
|
||||
log::info!("Namespace {} updated", ns_name);
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
pub mod delete;
|
||||
pub mod deploy;
|
||||
pub mod list;
|
||||
pub mod namespace;
|
||||
pub mod resolve;
|
||||
pub mod status;
|
||||
pub mod update;
|
||||
|
91
crates/faas-containerd/src/provider/function/namespace.rs
Normal file
91
crates/faas-containerd/src/provider/function/namespace.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use gateway::{handlers::namespace::NamespaceError, types::namespace::Namespace};
|
||||
|
||||
use crate::{
|
||||
impls::{backend, namespace::NamespaceServiceError},
|
||||
provider::ContainerdProvider,
|
||||
};
|
||||
|
||||
impl ContainerdProvider {
|
||||
pub(crate) async fn _create_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
labels: HashMap<String, String>,
|
||||
) -> Result<(), NamespaceError> {
|
||||
backend()
|
||||
.create_namespace(&namespace, labels)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
NamespaceServiceError::AlreadyExists => NamespaceError::AlreadyExists(format!(
|
||||
"namespace {} has been existed",
|
||||
namespace
|
||||
)),
|
||||
_ => NamespaceError::Internal(e.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
) -> Result<Namespace, NamespaceError> {
|
||||
let exist = backend()
|
||||
.namespace_exist(&namespace)
|
||||
.await
|
||||
.map_err(|e| NamespaceError::Internal(e.to_string()))?;
|
||||
if exist.is_none() {
|
||||
return Err(NamespaceError::NotFound(format!(
|
||||
"namespace {} not found",
|
||||
namespace
|
||||
)));
|
||||
}
|
||||
let ns = exist.unwrap();
|
||||
Ok(Namespace {
|
||||
name: Some(ns.name),
|
||||
labels: ns.labels,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _namespace_list(&self) -> Result<Vec<Namespace>, NamespaceError> {
|
||||
let ns_list = backend()
|
||||
.list_namespace()
|
||||
.await
|
||||
.map_err(|e| NamespaceError::Internal(e.to_string()))?;
|
||||
let mut ns_list_result = Vec::new();
|
||||
for ns in ns_list {
|
||||
ns_list_result.push(Namespace {
|
||||
name: Some(ns.name),
|
||||
labels: ns.labels,
|
||||
});
|
||||
}
|
||||
Ok(ns_list_result)
|
||||
}
|
||||
|
||||
pub(crate) async fn _delete_namespace(&self, namespace: String) -> Result<(), NamespaceError> {
|
||||
backend()
|
||||
.delete_namespace(&namespace)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
NamespaceServiceError::NotFound => {
|
||||
NamespaceError::NotFound(format!("namespace {} not found", namespace))
|
||||
}
|
||||
_ => NamespaceError::Internal(e.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _update_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
labels: HashMap<String, String>,
|
||||
) -> Result<(), NamespaceError> {
|
||||
backend()
|
||||
.update_namespace(&namespace, labels)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
NamespaceServiceError::NotFound => {
|
||||
NamespaceError::NotFound(format!("namespace {} not found", namespace))
|
||||
}
|
||||
_ => NamespaceError::Internal(e.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
pub mod function;
|
||||
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::{collections::HashMap, path::Path, sync::Arc};
|
||||
|
||||
use gateway::{
|
||||
handlers::function::{DeleteError, DeployError, ListError, ResolveError, UpdateError},
|
||||
handlers::{
|
||||
function::{DeleteError, DeployError, ListError, ResolveError, UpdateError},
|
||||
namespace::NamespaceError,
|
||||
},
|
||||
provider::Provider,
|
||||
types::function::{Deployment, Query, Status},
|
||||
types::{
|
||||
function::{Deployment, Query, Status},
|
||||
namespace::Namespace,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ContainerdProvider {
|
||||
@ -46,4 +51,32 @@ impl Provider for ContainerdProvider {
|
||||
async fn status(&self, function: Query) -> Result<Status, ResolveError> {
|
||||
self._status(function).await
|
||||
}
|
||||
|
||||
async fn create_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
labels: HashMap<String, String>,
|
||||
) -> Result<(), NamespaceError> {
|
||||
self._create_namespace(namespace, labels).await
|
||||
}
|
||||
|
||||
async fn update_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
labels: HashMap<String, String>,
|
||||
) -> Result<(), NamespaceError> {
|
||||
self._update_namespace(namespace, labels).await
|
||||
}
|
||||
|
||||
async fn delete_namespace(&self, namespace: String) -> Result<(), NamespaceError> {
|
||||
self._delete_namespace(namespace).await
|
||||
}
|
||||
|
||||
async fn get_namespace(&self, namespace: String) -> Result<Namespace, NamespaceError> {
|
||||
self._get_namespace(namespace).await
|
||||
}
|
||||
|
||||
async fn namespace_list(&self) -> Result<Vec<Namespace>, NamespaceError> {
|
||||
self._namespace_list().await
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,16 @@ pub fn config_app<P: Provider>(provider: Arc<P>) -> impl FnOnce(&mut ServiceConf
|
||||
.service(
|
||||
web::resource("/function/{functionName}")
|
||||
.route(web::get().to(handlers::function::status::<P>)),
|
||||
), // .service(
|
||||
)
|
||||
.service(
|
||||
web::resource("/namespace/{namespace}")
|
||||
.route(web::to(handlers::namespace::mut_namespace::<P>)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/namespaces")
|
||||
.route(web::get().to(handlers::namespace::namespace_list::<P>)),
|
||||
),
|
||||
// .service(
|
||||
// web::resource("/scale-function/{name}")
|
||||
// .route(web::post().to(handlers::scale_function)),
|
||||
// )
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod function;
|
||||
pub mod namespace;
|
||||
pub mod proxy;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
106
crates/gateway/src/handlers/namespace.rs
Normal file
106
crates/gateway/src/handlers/namespace.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::{provider::Provider, types::namespace::Namespace};
|
||||
use actix_http::{Method, StatusCode};
|
||||
use actix_web::{HttpRequest, HttpResponse, ResponseError, web};
|
||||
use derive_more::Display;
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum NamespaceError {
|
||||
#[display("Invalid: {}", _0)]
|
||||
Invalid(String),
|
||||
#[display("AlreadyExists: {}", _0)]
|
||||
AlreadyExists(String),
|
||||
#[display("NotFound: {}", _0)]
|
||||
NotFound(String),
|
||||
#[display("Internal: {}", _0)]
|
||||
Internal(String),
|
||||
#[display("MethodNotAllowed: {}", _0)]
|
||||
MethodNotAllowed(String),
|
||||
}
|
||||
|
||||
impl ResponseError for NamespaceError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
NamespaceError::Invalid(_) => StatusCode::BAD_REQUEST,
|
||||
NamespaceError::AlreadyExists(_) => StatusCode::BAD_REQUEST,
|
||||
NamespaceError::NotFound(_) => StatusCode::NOT_FOUND,
|
||||
NamespaceError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
NamespaceError::MethodNotAllowed(_) => StatusCode::METHOD_NOT_ALLOWED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mut_namespace<P: Provider>(
|
||||
req: HttpRequest,
|
||||
provider: web::Data<P>,
|
||||
info: Option<web::Json<Namespace>>,
|
||||
) -> Result<HttpResponse, NamespaceError> {
|
||||
let namespace = req.match_info().get("namespace");
|
||||
if namespace.is_none() {
|
||||
return Err(NamespaceError::Invalid("namespace is required".to_string()));
|
||||
}
|
||||
let namespace = namespace.unwrap();
|
||||
let labels;
|
||||
match *req.method() {
|
||||
Method::POST => {
|
||||
match info {
|
||||
Some(info) => {
|
||||
labels = info.0.labels;
|
||||
}
|
||||
None => {
|
||||
return Err(NamespaceError::Invalid(
|
||||
"Request body is required".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
(*provider)
|
||||
.create_namespace(namespace.to_string(), labels)
|
||||
.await
|
||||
.map(|_| {
|
||||
HttpResponse::Created()
|
||||
.body(format!("namespace {} was created successfully", namespace))
|
||||
})
|
||||
}
|
||||
Method::DELETE => (*provider)
|
||||
.delete_namespace(namespace.to_string())
|
||||
.await
|
||||
.map(|_| {
|
||||
HttpResponse::Accepted()
|
||||
.body(format!("namespace {} was deleted successfully", namespace))
|
||||
}),
|
||||
Method::PUT => {
|
||||
match info {
|
||||
Some(info) => {
|
||||
labels = info.0.labels;
|
||||
}
|
||||
None => {
|
||||
return Err(NamespaceError::Invalid(
|
||||
"Request body is required".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
(*provider)
|
||||
.update_namespace(namespace.to_string(), labels)
|
||||
.await
|
||||
.map(|_| {
|
||||
HttpResponse::Accepted()
|
||||
.body(format!("namespace {} was updated successfully", namespace))
|
||||
})
|
||||
}
|
||||
Method::GET => (*provider)
|
||||
.get_namespace(namespace.to_string())
|
||||
.await
|
||||
.map(|ns| HttpResponse::Ok().json(ns)),
|
||||
_ => Err(NamespaceError::MethodNotAllowed(
|
||||
"Method not allowed".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn namespace_list<P: Provider>(
|
||||
provider: web::Data<P>,
|
||||
) -> Result<HttpResponse, NamespaceError> {
|
||||
(*provider)
|
||||
.namespace_list()
|
||||
.await
|
||||
.map(|ns_list| HttpResponse::Ok().json(ns_list))
|
||||
}
|
@ -1,6 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
handlers::function::{DeleteError, DeployError, ListError, ResolveError, UpdateError},
|
||||
types::function::{Deployment, Query, Status},
|
||||
handlers::{
|
||||
function::{DeleteError, DeployError, ListError, ResolveError, UpdateError},
|
||||
namespace::NamespaceError,
|
||||
},
|
||||
types::{
|
||||
function::{Deployment, Query, Status},
|
||||
namespace::Namespace,
|
||||
},
|
||||
};
|
||||
|
||||
pub trait Provider: Send + Sync + 'static {
|
||||
@ -42,4 +50,30 @@ pub trait Provider: Send + Sync + 'static {
|
||||
&self,
|
||||
function: Query,
|
||||
) -> impl std::future::Future<Output = Result<Status, ResolveError>> + Send;
|
||||
|
||||
fn create_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
labels: HashMap<String, String>,
|
||||
) -> impl std::future::Future<Output = Result<(), NamespaceError>> + Send;
|
||||
|
||||
fn update_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
labels: HashMap<String, String>,
|
||||
) -> impl std::future::Future<Output = Result<(), NamespaceError>> + Send;
|
||||
|
||||
fn delete_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
) -> impl std::future::Future<Output = Result<(), NamespaceError>> + Send;
|
||||
|
||||
fn get_namespace(
|
||||
&self,
|
||||
namespace: String,
|
||||
) -> impl std::future::Future<Output = Result<Namespace, NamespaceError>> + Send;
|
||||
|
||||
fn namespace_list(
|
||||
&self,
|
||||
) -> impl std::future::Future<Output = Result<Vec<Namespace>, NamespaceError>> + Send;
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod config;
|
||||
pub mod function;
|
||||
pub mod namespace;
|
||||
|
8
crates/gateway/src/types/namespace.rs
Normal file
8
crates/gateway/src/types/namespace.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Namespace {
|
||||
pub name: Option<String>,
|
||||
pub labels: HashMap<String, String>,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user