minimum locking balance on deployment locking balance on app deployment refunding locked nano while error on daemon returning appropreate error on app deployment fixed some typos on logging new timeout constants for daemon respose minor change in schema and proto extensive tests on app deployments fixed some vm tests
400 lines
14 KiB
Rust
400 lines
14 KiB
Rust
use crate::constants::{ACCOUNT, APP_NODE, ID_ALPHABET, NEW_APP_REQ, NEW_VM_REQ, VM_NODE};
|
|
use crate::db::prelude as db;
|
|
use detee_shared::app_proto::AppNodeListResp;
|
|
use detee_shared::common_proto::MappedPort;
|
|
use detee_shared::general_proto::{Account, AccountBalance, ListOperatorsResp};
|
|
use detee_shared::{app_proto::*, vm_proto::*};
|
|
use nanoid::nanoid;
|
|
|
|
use surrealdb::RecordId;
|
|
|
|
impl From<db::Account> for AccountBalance {
|
|
fn from(account: db::Account) -> Self {
|
|
AccountBalance { balance: account.balance, tmp_locked: account.tmp_locked }
|
|
}
|
|
}
|
|
|
|
impl From<db::Account> for Account {
|
|
fn from(account: db::Account) -> Self {
|
|
Account {
|
|
pubkey: account.id.to_string(),
|
|
balance: account.balance,
|
|
tmp_locked: account.tmp_locked,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<NewVmReq> for db::NewVmReq {
|
|
fn from(new_vm_req: NewVmReq) -> Self {
|
|
Self {
|
|
id: RecordId::from((NEW_VM_REQ, nanoid!(40, &ID_ALPHABET))),
|
|
admin: RecordId::from((ACCOUNT, new_vm_req.admin_pubkey)),
|
|
vm_node: RecordId::from((VM_NODE, new_vm_req.node_pubkey)),
|
|
hostname: new_vm_req.hostname,
|
|
extra_ports: new_vm_req.extra_ports,
|
|
public_ipv4: new_vm_req.public_ipv4,
|
|
public_ipv6: new_vm_req.public_ipv6,
|
|
disk_size_gb: new_vm_req.disk_size_gb,
|
|
vcpus: new_vm_req.vcpus,
|
|
memory_mb: new_vm_req.memory_mb,
|
|
kernel_url: new_vm_req.kernel_url,
|
|
kernel_sha: new_vm_req.kernel_sha,
|
|
dtrfs_url: new_vm_req.dtrfs_url,
|
|
dtrfs_sha: new_vm_req.dtrfs_sha,
|
|
price_per_unit: new_vm_req.price_per_unit,
|
|
locked_nano: new_vm_req.locked_nano,
|
|
created_at: surrealdb::sql::Datetime::default(),
|
|
error: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::NewVmReq> for NewVmReq {
|
|
fn from(new_vm_req: db::NewVmReq) -> Self {
|
|
Self {
|
|
uuid: new_vm_req.id.key().to_string(),
|
|
hostname: new_vm_req.hostname,
|
|
admin_pubkey: new_vm_req.admin.key().to_string(),
|
|
node_pubkey: new_vm_req.vm_node.key().to_string(),
|
|
extra_ports: new_vm_req.extra_ports,
|
|
public_ipv4: new_vm_req.public_ipv4,
|
|
public_ipv6: new_vm_req.public_ipv6,
|
|
disk_size_gb: new_vm_req.disk_size_gb,
|
|
vcpus: new_vm_req.vcpus,
|
|
memory_mb: new_vm_req.memory_mb,
|
|
kernel_url: new_vm_req.kernel_url,
|
|
kernel_sha: new_vm_req.kernel_sha,
|
|
dtrfs_url: new_vm_req.dtrfs_url,
|
|
dtrfs_sha: new_vm_req.dtrfs_sha,
|
|
price_per_unit: new_vm_req.price_per_unit,
|
|
locked_nano: new_vm_req.locked_nano,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::WrappedMeasurement> for NewVmResp {
|
|
fn from(resp: db::WrappedMeasurement) -> Self {
|
|
match resp {
|
|
db::WrappedMeasurement::Args(uuid, args) => {
|
|
Self { uuid, error: String::new(), args: Some(args) }
|
|
}
|
|
db::WrappedMeasurement::Error(uuid, error) => NewVmResp { uuid, error, args: None },
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: NewVmResp is identical to UpdateVmResp so we can actually remove it from proto
|
|
impl From<db::WrappedMeasurement> for UpdateVmResp {
|
|
fn from(resp: db::WrappedMeasurement) -> Self {
|
|
match resp {
|
|
db::WrappedMeasurement::Args(uuid, args) => {
|
|
Self { uuid, error: String::new(), args: Some(args) }
|
|
}
|
|
db::WrappedMeasurement::Error(uuid, error) => Self { uuid, error, args: None },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<UpdateVmReq> for db::UpdateVmReq {
|
|
fn from(new_vm_req: UpdateVmReq) -> Self {
|
|
Self {
|
|
id: RecordId::from((NEW_VM_REQ, new_vm_req.uuid)),
|
|
admin: RecordId::from((ACCOUNT, new_vm_req.admin_pubkey)),
|
|
// vm_node gets modified later, and only if the db::UpdateVmReq is required
|
|
vm_node: RecordId::from((VM_NODE, String::new())),
|
|
disk_size_gb: new_vm_req.disk_size_gb,
|
|
vcpus: new_vm_req.vcpus,
|
|
memory_mb: new_vm_req.memory_mb,
|
|
kernel_url: new_vm_req.kernel_url,
|
|
kernel_sha: new_vm_req.kernel_sha,
|
|
dtrfs_url: new_vm_req.dtrfs_url,
|
|
dtrfs_sha: new_vm_req.dtrfs_sha,
|
|
created_at: surrealdb::sql::Datetime::default(),
|
|
error: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::UpdateVmReq> for UpdateVmReq {
|
|
fn from(update_vm_req: db::UpdateVmReq) -> Self {
|
|
Self {
|
|
uuid: update_vm_req.id.key().to_string(),
|
|
// daemon does not care about VM hostname
|
|
hostname: String::new(),
|
|
admin_pubkey: update_vm_req.admin.key().to_string(),
|
|
disk_size_gb: update_vm_req.disk_size_gb,
|
|
vcpus: update_vm_req.vcpus,
|
|
memory_mb: update_vm_req.memory_mb,
|
|
kernel_url: update_vm_req.kernel_url,
|
|
kernel_sha: update_vm_req.kernel_sha,
|
|
dtrfs_url: update_vm_req.dtrfs_url,
|
|
dtrfs_sha: update_vm_req.dtrfs_sha,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::DeletedVm> for DeleteVmReq {
|
|
fn from(delete_vm_req: db::DeletedVm) -> Self {
|
|
Self {
|
|
uuid: delete_vm_req.id.key().to_string(),
|
|
admin_pubkey: delete_vm_req.admin.key().to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::VmDaemonMsg> for BrainVmMessage {
|
|
fn from(notification: db::VmDaemonMsg) -> Self {
|
|
match notification {
|
|
db::VmDaemonMsg::Create(new_vm_req) => {
|
|
BrainVmMessage { msg: Some(brain_vm_message::Msg::NewVmReq(new_vm_req.into())) }
|
|
}
|
|
db::VmDaemonMsg::Update(update_vm_req) => BrainVmMessage {
|
|
msg: Some(brain_vm_message::Msg::UpdateVmReq(update_vm_req.into())),
|
|
},
|
|
db::VmDaemonMsg::Delete(deleted_vm) => {
|
|
BrainVmMessage { msg: Some(brain_vm_message::Msg::DeleteVm(deleted_vm.into())) }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::ActiveVmWithNode> for VmContract {
|
|
fn from(db_c: db::ActiveVmWithNode) -> Self {
|
|
let mut exposed_ports = Vec::new();
|
|
for port in db_c.mapped_ports.iter() {
|
|
exposed_ports.push(port.0);
|
|
}
|
|
VmContract {
|
|
uuid: db_c.id.key().to_string(),
|
|
hostname: db_c.hostname.clone(),
|
|
admin_pubkey: db_c.admin.key().to_string(),
|
|
node_pubkey: db_c.vm_node.id.key().to_string(),
|
|
node_ip: db_c.vm_node.ip.clone(),
|
|
location: format!(
|
|
"{}, {}, {}",
|
|
db_c.vm_node.city, db_c.vm_node.region, db_c.vm_node.country
|
|
),
|
|
memory_mb: db_c.memory_mb,
|
|
vcpus: db_c.vcpus,
|
|
disk_size_gb: db_c.disk_size_gb,
|
|
mapped_ports: db_c
|
|
.mapped_ports
|
|
.iter()
|
|
.map(|(h, g)| MappedPort { host_port: *h, guest_port: *g })
|
|
.collect(),
|
|
vm_public_ipv6: db_c.public_ipv6.clone(),
|
|
vm_public_ipv4: db_c.public_ipv4.clone(),
|
|
locked_nano: db_c.locked_nano,
|
|
dtrfs_sha: db_c.dtrfs_sha.clone(),
|
|
kernel_sha: db_c.kernel_sha.clone(),
|
|
nano_per_minute: db_c.price_per_minute(),
|
|
created_at: db_c.created_at.to_rfc3339(),
|
|
// TODO: remove updated_at from the proto
|
|
// This will get moved to VM history (users will be able to
|
|
// query old contracts, which also shows updates of existing contracts).
|
|
updated_at: db_c.created_at.to_rfc3339(),
|
|
collected_at: db_c.collected_at.to_rfc3339(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::Error> for tonic::Status {
|
|
fn from(e: db::Error) -> Self {
|
|
Self::internal(format!("Internal error: {e}"))
|
|
}
|
|
}
|
|
|
|
impl From<db::Operator> for ListOperatorsResp {
|
|
fn from(db_o: db::Operator) -> Self {
|
|
ListOperatorsResp {
|
|
pubkey: db_o.account.key().to_string(),
|
|
escrow: db_o.escrow,
|
|
email: db_o.email,
|
|
app_nodes: db_o.app_nodes,
|
|
vm_nodes: db_o.vm_nodes,
|
|
reports: db_o.reports,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::VmNodeWithReports> for VmNodeListResp {
|
|
fn from(vm_node: db::VmNodeWithReports) -> Self {
|
|
Self {
|
|
operator: vm_node.operator.key().to_string(),
|
|
node_pubkey: vm_node.id.key().to_string(),
|
|
country: vm_node.country,
|
|
region: vm_node.region,
|
|
city: vm_node.city,
|
|
ip: vm_node.ip,
|
|
reports: vm_node.reports.iter().map(|n| n.reason.clone()).collect(),
|
|
price: vm_node.price,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::AppNodeWithReports> for AppNodeListResp {
|
|
fn from(app_node: db::AppNodeWithReports) -> Self {
|
|
Self {
|
|
operator: app_node.operator.key().to_string(),
|
|
node_pubkey: app_node.id.key().to_string(),
|
|
country: app_node.country,
|
|
region: app_node.region,
|
|
city: app_node.city,
|
|
ip: app_node.ip,
|
|
reports: app_node.reports.iter().map(|n| n.reason.clone()).collect(),
|
|
price: app_node.price,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<VmNodeResources> for db::VmNodeResources {
|
|
fn from(res: VmNodeResources) -> Self {
|
|
Self {
|
|
avail_mem_mb: res.avail_memory_mb,
|
|
avail_vcpus: res.avail_vcpus,
|
|
avail_storage_gbs: res.avail_storage_gb,
|
|
avail_ipv4: res.avail_ipv4,
|
|
avail_ipv6: res.avail_ipv6,
|
|
avail_ports: res.avail_ports,
|
|
max_ports_per_vm: res.max_ports_per_vm,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::ActiveAppWithNode> for AppContract {
|
|
fn from(value: db::ActiveAppWithNode) -> Self {
|
|
let public_package_mr_enclave =
|
|
Some(hex::decode(value.mr_enclave.clone()).unwrap_or_default());
|
|
|
|
AppContract {
|
|
uuid: value.id.key().to_string(),
|
|
package_url: value.package_url,
|
|
admin_pubkey: value.admin.key().to_string(),
|
|
node_pubkey: value.app_node.id.key().to_string(),
|
|
public_ipv4: value.host_ipv4,
|
|
resource: Some(AppResource {
|
|
memory_mb: value.memory_mb,
|
|
disk_size_gb: value.disk_size_gb,
|
|
vcpus: value.vcpus,
|
|
ports: value.mapped_ports.iter().map(|(_, g)| *g).collect(),
|
|
}),
|
|
mapped_ports: value
|
|
.mapped_ports
|
|
.iter()
|
|
.map(|(h, g)| MappedPort { host_port: *h, guest_port: *g })
|
|
.collect(),
|
|
|
|
created_at: value.created_at.to_rfc3339(),
|
|
updated_at: value.created_at.to_rfc3339(),
|
|
nano_per_minute: value.price_per_unit,
|
|
locked_nano: value.locked_nano,
|
|
collected_at: value.collected_at.to_rfc3339(),
|
|
hratls_pubkey: value.mr_enclave,
|
|
public_package_mr_enclave,
|
|
app_name: value.app_name,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<NewAppReq> for db::NewAppReq {
|
|
fn from(val: NewAppReq) -> Self {
|
|
let resource = val.resource.unwrap_or_default();
|
|
|
|
let mr_enclave = val
|
|
.public_package_mr_enclave
|
|
.unwrap_or_default()
|
|
.iter()
|
|
.fold(String::new(), |acc, x| acc + &format!("{x:02x?}"));
|
|
|
|
Self {
|
|
id: RecordId::from((NEW_APP_REQ, nanoid!(40, &ID_ALPHABET))),
|
|
admin: RecordId::from((ACCOUNT, val.admin_pubkey)),
|
|
app_node: RecordId::from((APP_NODE, val.node_pubkey)),
|
|
app_name: val.app_name,
|
|
package_url: val.package_url,
|
|
mr_enclave,
|
|
hratls_pubkey: val.hratls_pubkey,
|
|
ports: resource.ports,
|
|
memory_mb: resource.memory_mb,
|
|
vcpus: resource.vcpus,
|
|
disk_size_gb: resource.disk_size_gb,
|
|
locked_nano: val.locked_nano,
|
|
price_per_unit: val.price_per_unit,
|
|
error: String::new(),
|
|
created_at: surrealdb::sql::Datetime::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::NewAppReq> for NewAppReq {
|
|
fn from(value: db::NewAppReq) -> Self {
|
|
let resource = AppResource {
|
|
vcpus: value.vcpus,
|
|
memory_mb: value.memory_mb,
|
|
disk_size_gb: value.disk_size_gb,
|
|
ports: value.ports,
|
|
};
|
|
let mr_enclave = Some(hex::decode(value.mr_enclave).unwrap_or_default());
|
|
|
|
Self {
|
|
package_url: value.package_url,
|
|
node_pubkey: value.app_node.key().to_string(),
|
|
resource: Some(resource),
|
|
uuid: value.id.key().to_string(),
|
|
admin_pubkey: value.admin.key().to_string(),
|
|
price_per_unit: value.price_per_unit,
|
|
locked_nano: value.locked_nano,
|
|
hratls_pubkey: value.hratls_pubkey,
|
|
public_package_mr_enclave: mr_enclave,
|
|
app_name: value.app_name,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::DeletedApp> for DelAppReq {
|
|
fn from(value: db::DeletedApp) -> Self {
|
|
Self { uuid: value.id.key().to_string(), admin_pubkey: value.admin.key().to_string() }
|
|
}
|
|
}
|
|
|
|
impl From<db::AppDaemonMsg> for BrainMessageApp {
|
|
fn from(value: db::AppDaemonMsg) -> Self {
|
|
match value {
|
|
db::AppDaemonMsg::Create(new_app_req) => {
|
|
BrainMessageApp { msg: Some(brain_message_app::Msg::NewAppReq(new_app_req.into())) }
|
|
}
|
|
db::AppDaemonMsg::Delete(del_app_req) => BrainMessageApp {
|
|
msg: Some(brain_message_app::Msg::DeleteAppReq(del_app_req.into())),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<AppNodeResources> for db::AppNodeResources {
|
|
fn from(value: AppNodeResources) -> Self {
|
|
Self {
|
|
avail_no_of_port: value.avail_no_of_port,
|
|
avail_vcpus: value.avail_vcpus,
|
|
avail_memory_mb: value.avail_memory_mb,
|
|
avail_storage_mb: value.avail_storage_mb,
|
|
max_ports_per_app: value.max_ports_per_app,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::ActiveApp> for NewAppRes {
|
|
fn from(val: db::ActiveApp) -> Self {
|
|
let mapped_ports = val
|
|
.mapped_ports
|
|
.iter()
|
|
.map(|(h, g)| MappedPort { host_port: *h, guest_port: *g })
|
|
.collect();
|
|
Self {
|
|
uuid: val.id.key().to_string(),
|
|
ip_address: val.host_ipv4,
|
|
mapped_ports,
|
|
error: String::new(),
|
|
}
|
|
}
|
|
}
|