mirror of
https://github.com/faas-rs/faasd-in-rust.git
synced 2025-06-08 15:56:48 +00:00
fix(api): some api's logic and response status code (#92)
This commit is contained in:
parent
90ea45c125
commit
702e6a8596
@ -2,10 +2,14 @@ use crate::{
|
||||
consts,
|
||||
handlers::{function_get::get_function, utils::CustomError},
|
||||
};
|
||||
use actix_web::{HttpResponse, Responder, ResponseError, error, web};
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use service::containerd_manager::ContainerdManager;
|
||||
|
||||
use super::function_list::Function;
|
||||
|
||||
// 参考响应状态:https://github.com/openfaas/faas/blob/7803ea1861f2a22adcbcfa8c79ed539bc6506d5b/api-docs/spec.openapi.yml#L141C2-L162C45
|
||||
// 请求体反序列化失败,自动返回400错误
|
||||
pub async fn delete_handler(info: web::Json<DeleteContainerInfo>) -> impl Responder {
|
||||
let function_name = info.function_name.clone();
|
||||
let namespace = info
|
||||
@ -13,44 +17,44 @@ pub async fn delete_handler(info: web::Json<DeleteContainerInfo>) -> impl Respon
|
||||
.clone()
|
||||
.unwrap_or_else(|| consts::DEFAULT_FUNCTION_NAMESPACE.to_string());
|
||||
|
||||
match delete(&function_name, &namespace).await {
|
||||
let namespaces = ContainerdManager::list_namespaces().await.unwrap();
|
||||
if !namespaces.contains(&namespace.to_string()) {
|
||||
return HttpResponse::NotFound().body(format!("Namespace '{}' does not exist", namespace));
|
||||
}
|
||||
|
||||
let function = match get_function(&function_name, &namespace).await {
|
||||
Ok(function) => function,
|
||||
Err(e) => {
|
||||
log::error!("Failed to get function: {}", e);
|
||||
return HttpResponse::NotFound()
|
||||
.body(format!("Function '{}' not found ", function_name));
|
||||
}
|
||||
};
|
||||
|
||||
match delete(&function, &namespace).await {
|
||||
Ok(()) => {
|
||||
HttpResponse::Ok().body(format!("Function {} deleted successfully.", function_name))
|
||||
}
|
||||
Err(e) => e.error_response(),
|
||||
Err(e) => {
|
||||
HttpResponse::InternalServerError().body(format!("Failed to delete function: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete(function_name: &str, namespace: &str) -> Result<(), CustomError> {
|
||||
let namespaces = ContainerdManager::list_namespaces().await.unwrap();
|
||||
if !namespaces.contains(&namespace.to_string()) {
|
||||
return Err(CustomError::ActixError(error::ErrorBadRequest(format!(
|
||||
"Namespace '{}' not valid or does not exist",
|
||||
namespace
|
||||
))));
|
||||
}
|
||||
let function = get_function(function_name, namespace).await.map_err(|e| {
|
||||
log::error!("Failed to get function: {}", e);
|
||||
CustomError::ActixError(error::ErrorNotFound(format!(
|
||||
"Function '{}' not found in namespace '{}'",
|
||||
function_name, namespace
|
||||
)))
|
||||
})?;
|
||||
async fn delete(function: &Function, namespace: &str) -> Result<(), CustomError> {
|
||||
let function_name = function.name.clone();
|
||||
if function.replicas != 0 {
|
||||
log::info!("function.replicas: {:?}", function.replicas);
|
||||
cni::delete_cni_network(namespace, function_name);
|
||||
cni::delete_cni_network(namespace, &function_name);
|
||||
log::info!("delete_cni_network ok");
|
||||
} else {
|
||||
log::info!("function.replicas: {:?}", function.replicas);
|
||||
}
|
||||
ContainerdManager::delete_container(function_name, namespace)
|
||||
ContainerdManager::delete_container(&function_name, namespace)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("Failed to delete container: {}", e);
|
||||
CustomError::ActixError(error::ErrorInternalServerError(format!(
|
||||
"Failed to delete container: {}",
|
||||
e
|
||||
)))
|
||||
CustomError::OtherError(format!("Failed to delete container: {}", e))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
use crate::{
|
||||
consts,
|
||||
handlers::utils::CustomError,
|
||||
types::function_deployment::{DeployFunctionInfo, FunctionDeployment},
|
||||
};
|
||||
use crate::{consts, handlers::utils::CustomError, types::function_deployment::DeployFunctionInfo};
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
|
||||
use service::{containerd_manager::ContainerdManager, image_manager::ImageManager};
|
||||
|
||||
// 参考响应状态 https://github.com/openfaas/faas/blob/7803ea1861f2a22adcbcfa8c79ed539bc6506d5b/api-docs/spec.openapi.yml#L121C1-L140C45
|
||||
// 请求体反序列化失败,自动返回400错误
|
||||
pub async fn deploy_handler(info: web::Json<DeployFunctionInfo>) -> impl Responder {
|
||||
let image = info.image.clone();
|
||||
let function_name = info.function_name.clone();
|
||||
@ -15,69 +13,64 @@ pub async fn deploy_handler(info: web::Json<DeployFunctionInfo>) -> impl Respond
|
||||
.clone()
|
||||
.unwrap_or(consts::DEFAULT_FUNCTION_NAMESPACE.to_string());
|
||||
|
||||
let config = FunctionDeployment {
|
||||
service: function_name,
|
||||
image,
|
||||
namespace: Some(namespace),
|
||||
log::info!("Namespace '{}' validated.", &namespace);
|
||||
|
||||
let container_list = match ContainerdManager::list_container_into_string(&namespace).await {
|
||||
Ok(container_list) => container_list,
|
||||
Err(e) => {
|
||||
log::error!("Failed to list container: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.body(format!("Failed to list container: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
match deploy(&config).await {
|
||||
if container_list.contains(&function_name) {
|
||||
return HttpResponse::BadRequest().body(format!(
|
||||
"Function '{}' already exists in namespace '{}'",
|
||||
function_name, namespace
|
||||
));
|
||||
}
|
||||
|
||||
match deploy(&function_name, &image, &namespace).await {
|
||||
Ok(()) => HttpResponse::Accepted().body(format!(
|
||||
"Function {} deployment initiated successfully.",
|
||||
config.service
|
||||
"Function {} deployment initiated successfully .",
|
||||
function_name
|
||||
)),
|
||||
Err(e) => HttpResponse::InternalServerError().body(format!(
|
||||
Err(e) => HttpResponse::BadRequest().body(format!(
|
||||
"failed to deploy function {}, because {}",
|
||||
config.service, e
|
||||
function_name, e
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn deploy(config: &FunctionDeployment) -> Result<(), CustomError> {
|
||||
let namespace = config.namespace.clone().unwrap();
|
||||
|
||||
log::info!(
|
||||
"Namespace '{}' validated.",
|
||||
config.namespace.clone().unwrap()
|
||||
);
|
||||
|
||||
let container_list = ContainerdManager::list_container_into_string(&namespace)
|
||||
.await
|
||||
.map_err(|e| CustomError::OtherError(format!("failed to list container:{}", e)))?;
|
||||
|
||||
if container_list.contains(&config.service) {
|
||||
return Err(CustomError::OtherError(
|
||||
"container has been existed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
ImageManager::prepare_image(&config.image, &namespace, true)
|
||||
async fn deploy(function_name: &str, image: &str, namespace: &str) -> Result<(), CustomError> {
|
||||
ImageManager::prepare_image(image, namespace, true)
|
||||
.await
|
||||
.map_err(CustomError::from)?;
|
||||
log::info!("Image '{}' validated ,", &config.image);
|
||||
log::info!("Image '{}' validated ,", image);
|
||||
|
||||
ContainerdManager::create_container(&config.image, &config.service, &namespace)
|
||||
ContainerdManager::create_container(image, function_name, namespace)
|
||||
.await
|
||||
.map_err(|e| CustomError::OtherError(format!("failed to create container:{}", e)))?;
|
||||
|
||||
log::info!(
|
||||
"Container {} created using image {} in namespace {}",
|
||||
&config.service,
|
||||
&config.image,
|
||||
function_name,
|
||||
image,
|
||||
namespace
|
||||
);
|
||||
|
||||
ContainerdManager::new_task(&config.service, &namespace)
|
||||
ContainerdManager::new_task(function_name, namespace)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
CustomError::OtherError(format!(
|
||||
"failed to start task for container {},{}",
|
||||
&config.service, e
|
||||
function_name, e
|
||||
))
|
||||
})?;
|
||||
log::info!(
|
||||
"Task for container {} was created successfully",
|
||||
&config.service
|
||||
function_name
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -23,43 +23,44 @@ pub struct Function {
|
||||
pub created_at: SystemTime,
|
||||
}
|
||||
|
||||
// openfaas API文档和faasd源码的响应不能完全对齐,这里参考源码的响应码设置
|
||||
// 考虑到部分操作可能返回500错误,但是faasd并没有做internal server error的处理(可能上层有中间件捕获),这里应该需要做500的处理
|
||||
pub async fn function_list_handler(req: HttpRequest) -> impl Responder {
|
||||
let namespace = req.match_info().get("namespace").unwrap_or("");
|
||||
if namespace.is_empty() {
|
||||
return HttpResponse::BadRequest().body("provide namespace in path");
|
||||
}
|
||||
match get_function_list(namespace).await {
|
||||
Ok(functions) => HttpResponse::Ok().body(serde_json::to_string(&functions).unwrap()),
|
||||
Err(e) => HttpResponse::from_error(e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_function_list(namespace: &str) -> Result<Vec<Function>, CustomError> {
|
||||
let namespaces = match ContainerdManager::list_namespaces().await {
|
||||
Ok(namespace) => namespace,
|
||||
Err(e) => {
|
||||
return Err(CustomError::OtherError(format!(
|
||||
"Failed to list namespaces:{}",
|
||||
e
|
||||
)));
|
||||
return HttpResponse::InternalServerError()
|
||||
.body(format!("Failed to list namespaces:{}", e));
|
||||
}
|
||||
};
|
||||
if !namespaces.contains(&namespace.to_string()) {
|
||||
return Err(CustomError::OtherError(format!(
|
||||
"Namespace '{}' not valid or does not exist",
|
||||
namespace
|
||||
)));
|
||||
return HttpResponse::BadRequest()
|
||||
.body(format!("Namespace '{}' does not exist", namespace));
|
||||
}
|
||||
|
||||
let container_list = match ContainerdManager::list_container_into_string(namespace).await {
|
||||
Ok(container_list) => container_list,
|
||||
Err(e) => {
|
||||
return Err(CustomError::OtherError(format!(
|
||||
"Failed to list container:{}",
|
||||
e
|
||||
)));
|
||||
return HttpResponse::InternalServerError()
|
||||
.body(format!("Failed to list container:{}", e));
|
||||
}
|
||||
};
|
||||
log::info!("container_list: {:?}", container_list);
|
||||
|
||||
match get_function_list(container_list, namespace).await {
|
||||
Ok(functions) => HttpResponse::Ok().body(serde_json::to_string(&functions).unwrap()),
|
||||
Err(e) => HttpResponse::BadRequest().body(format!("Failed to get function list: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_function_list(
|
||||
container_list: Vec<String>,
|
||||
namespace: &str,
|
||||
) -> Result<Vec<Function>, CustomError> {
|
||||
let mut functions: Vec<Function> = Vec::new();
|
||||
for cid in container_list {
|
||||
log::info!("cid: {}", cid);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::consts::DEFAULT_FUNCTION_NAMESPACE;
|
||||
use crate::handlers::function_get::get_function;
|
||||
use actix_web::{Error, error::ErrorInternalServerError};
|
||||
use actix_web::{Error, error::ErrorInternalServerError, error::ErrorServiceUnavailable};
|
||||
use log;
|
||||
use url::Url;
|
||||
|
||||
@ -23,7 +23,7 @@ impl InvokeResolver {
|
||||
Ok(function) => function,
|
||||
Err(e) => {
|
||||
log::error!("Failed to get function:{}", e);
|
||||
return Err(ErrorInternalServerError("Failed to get function"));
|
||||
return Err(ErrorServiceUnavailable("Failed to get function"));
|
||||
}
|
||||
};
|
||||
log::info!("Function:{:?}", function);
|
||||
|
@ -2,13 +2,19 @@ use crate::handlers::invoke_resolver::InvokeResolver;
|
||||
use crate::proxy::builder::build_proxy_request;
|
||||
use crate::proxy::client::new_proxy_client_from_config;
|
||||
use crate::types::config::FaaSConfig;
|
||||
use actix_web::{Error, HttpRequest, HttpResponse, Responder, http::Method, web};
|
||||
use actix_web::{
|
||||
Error, HttpRequest, HttpResponse,
|
||||
error::{ErrorBadRequest, ErrorInternalServerError, ErrorMethodNotAllowed},
|
||||
http::Method,
|
||||
web,
|
||||
};
|
||||
|
||||
// 主要参考源码的响应设置
|
||||
pub async fn proxy_handler(
|
||||
config: web::Data<FaaSConfig>,
|
||||
req: HttpRequest,
|
||||
payload: web::Payload,
|
||||
) -> impl Responder {
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let proxy_client = new_proxy_client_from_config(config.as_ref()).await;
|
||||
log::info!("proxy_client : {:?}", proxy_client);
|
||||
|
||||
@ -19,11 +25,8 @@ pub async fn proxy_handler(
|
||||
| Method::GET
|
||||
| Method::PATCH
|
||||
| Method::HEAD
|
||||
| Method::OPTIONS => match proxy_request(&req, payload, &proxy_client).await {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => HttpResponse::from_error(e),
|
||||
},
|
||||
_ => HttpResponse::MethodNotAllowed().body("method not allowed"),
|
||||
| Method::OPTIONS => proxy_request(&req, payload, &proxy_client).await,
|
||||
_ => Err(ErrorMethodNotAllowed("method not allowed")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,13 +38,10 @@ async fn proxy_request(
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let function_name = req.match_info().get("name").unwrap_or("");
|
||||
if function_name.is_empty() {
|
||||
return Ok(HttpResponse::BadRequest().body("provide function name in path"));
|
||||
return Err(ErrorBadRequest("function name is required"));
|
||||
}
|
||||
|
||||
let function_addr = match InvokeResolver::resolve_function_url(function_name).await {
|
||||
Ok(function_addr) => function_addr,
|
||||
Err(e) => return Ok(HttpResponse::BadRequest().body(e.to_string())),
|
||||
};
|
||||
let function_addr = InvokeResolver::resolve_function_url(function_name).await?;
|
||||
|
||||
let proxy_req = build_proxy_request(req, &function_addr, proxy_client, payload).await?;
|
||||
|
||||
@ -58,6 +58,6 @@ async fn proxy_request(
|
||||
|
||||
Ok(client_resp.body(body))
|
||||
}
|
||||
Err(e) => Ok(HttpResponse::BadGateway().body(e.to_string())),
|
||||
Err(e) => Err(ErrorInternalServerError(e)),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user