detee-cli/src/snp/update.rs
2025-06-25 19:20:31 +05:30

105 lines
3.8 KiB
Rust

// SPDX-License-Identifier: Apache-2.0
use super::grpc::{self, proto};
use super::{injector, Dtrfs, Error};
use crate::config::Config;
use crate::utils::block_on;
use log::{debug, info};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Request {
hostname: String,
vcpus: u32,
memory_mib: u32,
disk_size_mib: u32,
dtrfs: Option<Dtrfs>,
}
impl Request {
pub fn process_request(
hostname: String,
uuid: &str,
vcpus: u32,
memory_mb: u32,
disk_size_gb: u32,
dtrfs: &str,
) -> Result<(), Error> {
let dtrfs = match dtrfs {
"" => None,
"latest" => Some(super::DEFAULT_DTRFS.clone()),
path => {
log::info!("Loading DTRFS from path: {path}");
Some(Dtrfs::load_from_file(path)?)
}
};
let req =
Self { hostname, vcpus, memory_mib: memory_mb, disk_size_mib: disk_size_gb, dtrfs };
if req == Self::default() {
log::info!("Skipping hardware upgrade (no arguments specified).");
return Ok(());
}
log::info!("Starting VM updated based on req: {req:#?}");
req.update(uuid)
}
fn update(&self, uuid: &str) -> Result<(), Error> {
info!("Starting the process of updating the VM. {self:?}");
let update_vm_resp = self.send_update_vm_request(uuid)?;
debug!("The response for Update VM is: {update_vm_resp:#?}");
if !update_vm_resp.error.is_empty() {
return Err(Error::Node(update_vm_resp.error));
}
eprintln!("The modifications got approved. Proceeding with update...");
let updated_contract = block_on(grpc::get_contract_by_uuid(uuid))?;
debug!("Got the current contract for the VM after update. {updated_contract:#?}");
if !(self.vcpus != 0 || self.memory_mib != 0 || self.dtrfs.is_some()) {
eprintln!("vCPUs and kernel did not get modified. Secret injection is not required.");
return Ok(());
}
let args = update_vm_resp.args.ok_or(Error::NoMeasurement)?;
let measurement_args = injector::Args {
uuid: update_vm_resp.uuid,
vcpus: updated_contract.vcpus,
kernel: updated_contract.kernel_sha,
initrd: updated_contract.dtrfs_sha,
args: args.clone(),
};
let measurement = measurement_args.get_measurement()?;
injector::execute(measurement, args.dtrfs_api_endpoint, None, &updated_contract.hostname)?;
Ok(())
}
// returns node IP and data regarding the new VM
fn send_update_vm_request(&self, uuid: &str) -> Result<proto::UpdateVmResp, Error> {
let (kernel_url, kernel_sha, dtrfs_url, dtrfs_sha) = match self.dtrfs.clone() {
Some(dtrfs) => (dtrfs.kernel_url, dtrfs.kernel_sha, dtrfs.dtrfs_url, dtrfs.dtrfs_sha),
None => (String::new(), String::new(), String::new(), String::new()),
};
Ok(block_on(grpc::update_vm(proto::UpdateVmReq {
uuid: uuid.to_string(),
hostname: self.hostname.clone(),
admin_pubkey: Config::get_detee_wallet()?,
disk_size_mib: self.disk_size_mib * 1024,
vcpus: self.vcpus,
memory_mib: self.memory_mib * 1024,
kernel_url,
kernel_sha,
dtrfs_url,
dtrfs_sha,
}))?)
}
}
pub fn expand_vm_hours(uuid: &str, hours: u32) -> Result<(), Error> {
let contract = super::get_one_contract(uuid)?;
// vcpus: u32, memory_mb: u32, disk_size_gb: u32, public_ipv4: bool, hours: u32, node_price:
let locked_nano = contract.nano_per_minute * 60 * (hours as u64);
block_on(grpc::extend_vm(uuid.to_string(), Config::get_detee_wallet()?, locked_nano))?;
Ok(())
}