215 lines
6.3 KiB
Rust
215 lines
6.3 KiB
Rust
use std::convert::TryInto;
|
|
|
|
use crate::entity::*;
|
|
use crate::macros::api_err;
|
|
use crate::types::MacAddr;
|
|
use crate::types::*;
|
|
use crate::yui::auth::*;
|
|
use crate::yui::*;
|
|
use crate::Instances;
|
|
use rocket::serde::json::Json;
|
|
use rocket_db_pools::Connection;
|
|
use sea_orm::ActiveValue::{NotSet, Set};
|
|
use sea_orm::QuerySelect;
|
|
use sea_orm::{
|
|
ActiveModelTrait, ColumnTrait, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
|
|
};
|
|
|
|
/// Create a new instance
|
|
#[post("/instances", format = "application/json", data = "<iinfo>")]
|
|
pub async fn new_instance(
|
|
db: Connection<Instances>,
|
|
iinfo: Json<InstanceReq>,
|
|
_api: APIUser,
|
|
) -> Result<Json<InstanceResponse>, APIError> {
|
|
let iinfo: InstanceReq = iinfo.into_inner();
|
|
let ii_name = iinfo.name.clone();
|
|
|
|
// loosely validate ssh keys
|
|
if let Some(ssh_keys) = &iinfo.ssh_keys {
|
|
for (i, key) in ssh_keys.iter().enumerate() {
|
|
if key.contains('\n') {
|
|
return Err(api_err!(
|
|
BadRequest,
|
|
"SSH Key {} contains newlines. Ensure SSH keys are split as an array",
|
|
i
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// validation: confirm hostname isn't in use
|
|
if instance_info::Entity::find()
|
|
.filter(instance_info::Column::Hostname.eq(ii_name))
|
|
.count(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Database access failed: {:?}", e))?
|
|
!= 0
|
|
{
|
|
return Err(api_err!(BadRequest, "Hostname already exists"));
|
|
}
|
|
|
|
// validation: confirm mac address isn't in use
|
|
let inst_mac = MacAddr::try_from(iinfo.mac_address)
|
|
.map_err(|_| api_err!(BadRequest, "MAC address is invalid"))?;
|
|
if instance_info::Entity::find()
|
|
.filter(instance_info::Column::MacAddress.eq(inst_mac.to_string()))
|
|
.count(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Database access failed: {:?}", e))?
|
|
!= 0
|
|
{
|
|
return Err(api_err!(Conflict, "MAC Address is in use"));
|
|
}
|
|
|
|
// create and commit the instance info
|
|
let newid: u32 = (inst_mac[3] as u32) << 24
|
|
| (inst_mac[4] as u32) << 16
|
|
| (inst_mac[5] as u32) << 8
|
|
| (rand::random::<u8>() as u32);
|
|
let iisql = instance_info::ActiveModel {
|
|
id: Set(newid),
|
|
hostname: Set(iinfo.name.clone()),
|
|
mac_address: Set(inst_mac.to_string()),
|
|
ssh_keys: match iinfo.ssh_keys {
|
|
Some(keys) => Set(Some(keys.join("\n"))),
|
|
None => NotSet,
|
|
},
|
|
user_data: Set(iinfo
|
|
.user_data
|
|
.map(base64::decode)
|
|
.map_or(Ok(None), |v| v.map(Some))
|
|
.map_err(|e| api_err!(BadRequest, "Error decoding user-data: {:?}", e))?),
|
|
};
|
|
|
|
match iisql.insert(&*db).await {
|
|
Ok(v) => Ok(Json(InstanceResponse::new(vec![v]))),
|
|
Err(err) => Err(api_err!(
|
|
InternalServerError,
|
|
"Failed to add record: {:?}",
|
|
err
|
|
)),
|
|
}
|
|
}
|
|
|
|
/// List all instances in the database
|
|
#[get("/instances")]
|
|
pub async fn get_instances(
|
|
db: Connection<Instances>,
|
|
_api: APIUser,
|
|
) -> Result<Json<InstanceResponse>, APIError> {
|
|
match instance_info::Entity::find()
|
|
.column(instance_info::Column::Id)
|
|
.column(instance_info::Column::MacAddress)
|
|
.column(instance_info::Column::Hostname)
|
|
.all(&*db)
|
|
.await
|
|
{
|
|
Ok(v) => Ok(Json(InstanceResponse::new(v))),
|
|
Err(err) => Err(api_err!(
|
|
InternalServerError,
|
|
"Failed to get records: {:?}",
|
|
err
|
|
)),
|
|
}
|
|
}
|
|
|
|
/// Get a specified instance
|
|
#[get("/instances/<id>")]
|
|
pub async fn get_instance(
|
|
db: Connection<Instances>,
|
|
id: u32,
|
|
_api: APIUser,
|
|
) -> Result<Json<InstanceResponse>, APIError> {
|
|
match instance_info::Entity::find_by_id(id)
|
|
.one(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Database error: {:?}", e))?
|
|
{
|
|
Some(v) => Ok(Json(InstanceResponse::new(vec![v]))),
|
|
None => Err(api_err!(NotFound, "Instance not found")),
|
|
}
|
|
}
|
|
|
|
/// Delete an instance from the database
|
|
#[delete("/instances/<id>")]
|
|
pub async fn delete_instance(
|
|
db: Connection<Instances>,
|
|
id: u32,
|
|
_api: APIUser,
|
|
) -> Result<(), APIError> {
|
|
match instance_info::Entity::find_by_id(id)
|
|
.one(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Error deleting instance: {:?}", e))?
|
|
{
|
|
Some(ent) => {
|
|
ent.delete(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Error deleting instance: {:?}", e))?;
|
|
Ok(())
|
|
}
|
|
None => Err(api_err!(NotFound, "Instance not found")),
|
|
}
|
|
}
|
|
|
|
// Set a default (primarily, ssh-keys)
|
|
#[post("/config/<key>", format = "plain", data = "<val>")]
|
|
async fn set_defaults(
|
|
db: Connection<Instances>,
|
|
key: String,
|
|
val: String,
|
|
_api: APIUser,
|
|
) -> Result<(), APIError> {
|
|
let key: DefaultKey = key
|
|
.try_into()
|
|
.map_err(|_| api_err!(BadRequest, "Invalid default key"))?;
|
|
match defaults::Entity::find_by_id(key)
|
|
.one(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Error getting default: {:?}", e))?
|
|
{
|
|
Some(def) => {
|
|
let mut model: defaults::ActiveModel = def.into();
|
|
model.value = Set(val);
|
|
model.update(&*db).await.map(|_| ())
|
|
}
|
|
None => {
|
|
let model = defaults::ActiveModel {
|
|
key: Set(key),
|
|
value: Set(val),
|
|
};
|
|
model.insert(&*db).await.map(|_| ())
|
|
}
|
|
}
|
|
.map_err(|e| api_err!(InternalServerError, "Error setting `{}`: {:?}", key, e))
|
|
}
|
|
|
|
// get a default setting
|
|
#[get("/config/<key>")]
|
|
async fn get_defaults(
|
|
db: Connection<Instances>,
|
|
key: String,
|
|
_api: APIUser,
|
|
) -> Result<String, APIError> {
|
|
let key: DefaultKey = key
|
|
.try_into()
|
|
.map_err(|_| api_err!(BadRequest, "Invalid default key"))?;
|
|
defaults::Entity::find_by_id(key)
|
|
.one(&*db)
|
|
.await
|
|
.map_err(|e| api_err!(InternalServerError, "Error getting default: {:?}", e))?
|
|
.map_or(Err(api_err!(NotFound, "Key not yet set")), |v| Ok(v.value))
|
|
}
|
|
|
|
pub fn all() -> Vec<rocket::Route> {
|
|
routes![
|
|
new_instance,
|
|
get_instances,
|
|
get_instance,
|
|
delete_instance,
|
|
set_defaults,
|
|
get_defaults,
|
|
]
|
|
}
|