From d397faf1ad2a53e44c50f7749c0690707bc95580 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 17 Nov 2023 11:35:35 +0100 Subject: [PATCH] feat: delete satellites and orbiters Signed-off-by: David Dal Busco --- src/mission_control/src/lib.rs | 22 +++++++- src/mission_control/src/mgmt/canister.rs | 3 +- src/mission_control/src/segments/canister.rs | 27 ++++++++- src/mission_control/src/segments/orbiter.rs | 19 ++++++- src/mission_control/src/segments/satellite.rs | 24 +++++++- src/mission_control/src/segments/store.rs | 29 +++++++++- src/observatory/src/lib.rs | 3 +- src/observatory/src/store.rs | 2 +- src/orbiter/src/lib.rs | 12 +++- src/satellite/src/lib.rs | 12 +++- src/shared/src/ic.rs | 55 ++++++++++++++++++- src/shared/src/types.rs | 8 ++- 12 files changed, 198 insertions(+), 18 deletions(-) diff --git a/src/mission_control/src/lib.rs b/src/mission_control/src/lib.rs index a6c9178b6..34b391a6d 100644 --- a/src/mission_control/src/lib.rs +++ b/src/mission_control/src/lib.rs @@ -23,8 +23,12 @@ use crate::guards::{ }; use crate::mgmt::canister::top_up_canister; use crate::mgmt::status::collect_statuses; -use crate::segments::orbiter::create_orbiter as create_orbiter_console; -use crate::segments::satellite::create_satellite as create_satellite_console; +use crate::segments::orbiter::{ + create_orbiter as create_orbiter_console, delete_orbiter as delete_orbiter_segment, +}; +use crate::segments::satellite::{ + create_satellite as create_satellite_console, delete_satellite as delete_satellite_segment, +}; use crate::segments::store::get_orbiters; use crate::store::{ get_user as get_user_store, @@ -162,6 +166,13 @@ async fn del_satellites_controllers(satellite_ids: Vec, controllers } } +#[update(guard = "caller_is_user_or_admin_controller")] +async fn delete_satellite(satellite_id: SatelliteId, cycles_to_retain: u128) { + delete_satellite_segment(&satellite_id, cycles_to_retain) + .await + .unwrap_or_else(|e| trap(&e)); +} + /// Orbiters #[query(guard = "caller_is_user_or_admin_controller")] @@ -203,6 +214,13 @@ async fn del_orbiters_controllers(orbiter_ids: Vec, controllers: Vec< } } +#[update(guard = "caller_is_user_or_admin_controller")] +async fn delete_orbiter(orbiter_id: OrbiterId, cycles_to_retain: u128) { + delete_orbiter_segment(&orbiter_id, cycles_to_retain) + .await + .unwrap_or_else(|e| trap(&e)); +} + /// Mgmt #[update(guard = "caller_is_user_or_admin_controller")] diff --git a/src/mission_control/src/mgmt/canister.rs b/src/mission_control/src/mgmt/canister.rs index 93f6028cd..ac2e90f39 100644 --- a/src/mission_control/src/mgmt/canister.rs +++ b/src/mission_control/src/mgmt/canister.rs @@ -1,5 +1,6 @@ use candid::Principal; use ic_cdk::api::call::CallResult; +use ic_cdk::api::management_canister::main::CanisterId; use ic_cdk::call; use ic_ledger_types::{Subaccount, Tokens}; use shared::constants::{IC_TRANSACTION_FEE_ICP, MEMO_CANISTER_TOP_UP}; @@ -7,7 +8,7 @@ use shared::env::CMC; use shared::ledger::transfer_payment; use shared::types::cmc::{Cycles, NotifyError, TopUpCanisterArgs}; -pub async fn top_up_canister(canister_id: &Principal, amount: &Tokens) -> Result<(), String> { +pub async fn top_up_canister(canister_id: &CanisterId, amount: &Tokens) -> Result<(), String> { // We need to hold back 1 transaction fee for the 'send' and also 1 for the 'notify' let send_amount = Tokens::from_e8s(amount.e8s() - (2 * IC_TRANSACTION_FEE_ICP.e8s())); diff --git a/src/mission_control/src/segments/canister.rs b/src/mission_control/src/segments/canister.rs index a03910c1d..9f5a1dca1 100644 --- a/src/mission_control/src/segments/canister.rs +++ b/src/mission_control/src/segments/canister.rs @@ -1,12 +1,13 @@ use crate::store::get_user; use candid::Principal; use ic_cdk::api::call::CallResult; -use ic_cdk::call; +use ic_cdk::{call, id}; use ic_ledger_types::{BlockIndex, Tokens}; use shared::constants::{IC_TRANSACTION_FEE_ICP, MEMO_CANISTER_CREATE}; use shared::env::CONSOLE; +use shared::ic::{delete_segment, stop_segment}; use shared::ledger::{transfer_payment, SUB_ACCOUNT}; -use shared::types::interface::GetCreateCanisterFeeArgs; +use shared::types::interface::{DepositCyclesArgs, GetCreateCanisterFeeArgs}; use shared::types::state::UserId; use std::future::Future; @@ -51,3 +52,25 @@ where } } } + +pub async fn delete_canister(segment_id: &Principal, cycles_to_retain: u128) -> Result<(), String> { + deposit_cycles(segment_id, cycles_to_retain).await?; + + stop_segment(*segment_id).await?; + + delete_segment(*segment_id).await +} + +async fn deposit_cycles(segment_id: &Principal, cycles_to_retain: u128) -> Result<(), String> { + let args = DepositCyclesArgs { + destination_id: id(), + cycles_to_retain, + }; + + let result: CallResult<((),)> = call(*segment_id, "deposit_cycles", (args,)).await; + + match result { + Err((_, message)) => Err(["Deposit cycles failed.", &message].join(" - ")), + Ok(_) => Ok(()), + } +} diff --git a/src/mission_control/src/segments/orbiter.rs b/src/mission_control/src/segments/orbiter.rs index bde77f237..d13239a24 100644 --- a/src/mission_control/src/segments/orbiter.rs +++ b/src/mission_control/src/segments/orbiter.rs @@ -1,5 +1,5 @@ -use crate::segments::canister::create_canister; -use crate::segments::store::add_orbiter; +use crate::segments::canister::{create_canister, delete_canister}; +use crate::segments::store::{add_orbiter, delete_orbiter as delete_orbiter_store, get_orbiter}; use crate::types::state::Orbiter; use candid::Principal; use ic_cdk::api::call::CallResult; @@ -13,6 +13,21 @@ pub async fn create_orbiter(name: &Option) -> Result { create_canister("get_create_orbiter_fee", create_and_save_orbiter, name).await } +pub async fn delete_orbiter(orbiter_id: &OrbiterId, cycles_to_retain: u128) -> Result<(), String> { + let orbiter = get_orbiter(orbiter_id); + + match orbiter { + None => Err("Orbiter not found or not owned by this mission control.".to_string()), + Some(_) => { + delete_canister(orbiter_id, cycles_to_retain).await?; + + delete_orbiter_store(orbiter_id); + + Ok(()) + } + } +} + async fn create_and_save_orbiter( user: UserId, name: Option, diff --git a/src/mission_control/src/segments/satellite.rs b/src/mission_control/src/segments/satellite.rs index be6c98dce..140ac03c5 100644 --- a/src/mission_control/src/segments/satellite.rs +++ b/src/mission_control/src/segments/satellite.rs @@ -1,5 +1,7 @@ -use crate::segments::canister::create_canister; -use crate::segments::store::add_satellite; +use crate::segments::canister::{create_canister, delete_canister}; +use crate::segments::store::{ + add_satellite, delete_satellite as delete_satellite_store, get_satellite, +}; use crate::types::state::Satellite; use candid::Principal; use ic_cdk::api::call::CallResult; @@ -18,6 +20,24 @@ pub async fn create_satellite(name: &str) -> Result { .await } +pub async fn delete_satellite( + satellite_id: &SatelliteId, + cycles_to_retain: u128, +) -> Result<(), String> { + let satellite = get_satellite(satellite_id); + + match satellite { + None => Err("Satellite not found or not owned by this mission control.".to_string()), + Some(_) => { + delete_canister(satellite_id, cycles_to_retain).await?; + + delete_satellite_store(satellite_id); + + Ok(()) + } + } +} + async fn create_and_save_satellite( user: UserId, name: Option, diff --git a/src/mission_control/src/segments/store.rs b/src/mission_control/src/segments/store.rs index 0d4a644c7..8885fc32e 100644 --- a/src/mission_control/src/segments/store.rs +++ b/src/mission_control/src/segments/store.rs @@ -11,6 +11,14 @@ pub fn get_satellites() -> Satellites { STATE.with(|state| state.borrow().stable.satellites.clone()) } +pub fn get_satellite(satellite_id: &SatelliteId) -> Option { + STATE.with(|state| get_segment_impl(satellite_id, &state.borrow().stable.satellites)) +} + +pub fn delete_satellite(satellite_id: &SatelliteId) -> Option { + STATE.with(|state| delete_segment_impl(satellite_id, &mut state.borrow_mut().stable.satellites)) +} + pub fn add_satellite(satellite_id: &SatelliteId, name: &Option) -> Satellite { STATE.with(|state| { add_segment_impl( @@ -40,7 +48,15 @@ pub fn get_orbiters() -> Orbiters { STATE.with(|state| state.borrow().stable.orbiters.clone()) } -pub fn add_orbiter(orbiter_id: &SatelliteId, name: &Option) -> Orbiter { +pub fn get_orbiter(orbiter_id: &OrbiterId) -> Option { + STATE.with(|state| get_segment_impl(orbiter_id, &state.borrow().stable.orbiters)) +} + +pub fn delete_orbiter(orbiter_id: &OrbiterId) -> Option { + STATE.with(|state| delete_segment_impl(orbiter_id, &mut state.borrow_mut().stable.orbiters)) +} + +pub fn add_orbiter(orbiter_id: &OrbiterId, name: &Option) -> Orbiter { STATE.with(|state| { add_segment_impl( orbiter_id, @@ -75,6 +91,17 @@ fn add_segment_impl( value.clone() } +fn delete_segment_impl( + id: &K, + state: &mut HashMap, +) -> Option { + state.remove(id) +} + +fn get_segment_impl(id: &K, state: &HashMap) -> Option { + state.get(id).cloned() +} + fn set_metadata_impl>( id: &K, metadata: &Metadata, diff --git a/src/observatory/src/lib.rs b/src/observatory/src/lib.rs index 56526b60a..65e0b05a6 100644 --- a/src/observatory/src/lib.rs +++ b/src/observatory/src/lib.rs @@ -12,9 +12,8 @@ use crate::cron_jobs::cron_jobs; use crate::guards::{caller_can_execute_cron_jobs, caller_is_admin_controller}; use crate::reports::collect_statuses as collect_statuses_report; use crate::store::{ - delete_controllers, get_cron_tab as get_cron_tab_store, + delete_controllers, get_cron_tab as get_cron_tab_store, get_statuses as get_statuses_store, set_controllers as set_controllers_store, set_cron_tab as set_cron_tab_store, - get_statuses as get_statuses_store }; use crate::types::interface::{ListStatuses, ListStatusesArgs, SetCronTab}; use crate::types::state::{Archive, ArchiveStatuses, CronTab, StableState, State}; diff --git a/src/observatory/src/store.rs b/src/observatory/src/store.rs index b0ec349ea..d7f57c0e6 100644 --- a/src/observatory/src/store.rs +++ b/src/observatory/src/store.rs @@ -129,4 +129,4 @@ pub fn get_statuses(user: &UserId) -> Option { fn get_statuses_impl(user: &UserId, state: &StableState) -> Option { let statuses = state.archive.statuses.get(user); statuses.cloned() -} \ No newline at end of file +} diff --git a/src/orbiter/src/lib.rs b/src/orbiter/src/lib.rs index 80ef22db1..3c5cebfb2 100644 --- a/src/orbiter/src/lib.rs +++ b/src/orbiter/src/lib.rs @@ -44,7 +44,10 @@ use shared::constants::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; use shared::controllers::{ assert_max_number_of_controllers, assert_no_anonymous_controller, init_controllers, }; -use shared::types::interface::{DeleteControllersArgs, SegmentArgs, SetControllersArgs}; +use shared::ic::deposit_cycles as deposit_cycles_shared; +use shared::types::interface::{ + DeleteControllersArgs, DepositCyclesArgs, SegmentArgs, SetControllersArgs, +}; use shared::types::state::{ControllerScope, Controllers, SatelliteId}; use std::mem; @@ -267,6 +270,13 @@ fn list_satellite_configs() -> SatelliteConfigs { /// Mgmt +#[update(guard = "caller_is_admin_controller")] +async fn deposit_cycles(args: DepositCyclesArgs) { + deposit_cycles_shared(args) + .await + .unwrap_or_else(|e| trap(&e)) +} + #[query] fn version() -> String { env!("CARGO_PKG_VERSION").to_string() diff --git a/src/satellite/src/lib.rs b/src/satellite/src/lib.rs index ef7dc8605..dda9c1414 100644 --- a/src/satellite/src/lib.rs +++ b/src/satellite/src/lib.rs @@ -61,7 +61,10 @@ use shared::constants::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; use shared::controllers::{ assert_max_number_of_controllers, assert_no_anonymous_controller, init_controllers, }; -use shared::types::interface::{DeleteControllersArgs, SegmentArgs, SetControllersArgs}; +use shared::ic::deposit_cycles as deposit_cycles_shared; +use shared::types::interface::{ + DeleteControllersArgs, DepositCyclesArgs, SegmentArgs, SetControllersArgs, +}; use shared::types::state::{ControllerScope, Controllers}; use std::mem; use storage::http::types::{ @@ -454,6 +457,13 @@ fn del_assets(collection: CollectionKey) { /// Mgmt +#[update(guard = "caller_is_admin_controller")] +async fn deposit_cycles(args: DepositCyclesArgs) { + deposit_cycles_shared(args) + .await + .unwrap_or_else(|e| trap(&e)) +} + #[query] fn version() -> String { env!("CARGO_PKG_VERSION").to_string() diff --git a/src/shared/src/ic.rs b/src/shared/src/ic.rs index a8023271b..51091ed2d 100644 --- a/src/shared/src/ic.rs +++ b/src/shared/src/ic.rs @@ -1,14 +1,16 @@ use crate::constants::CREATE_CANISTER_CYCLES; use crate::types::ic::WasmArg; +use crate::types::interface::DepositCyclesArgs; use crate::types::state::{SegmentStatus, SegmentStatusResult}; use candid::Principal; use ic_cdk::api::call::CallResult; use ic_cdk::api::management_canister::main::{ - canister_status as ic_canister_status, create_canister, install_code as ic_install_code, + canister_status as ic_canister_status, create_canister, delete_canister, + deposit_cycles as ic_deposit_cycles, install_code as ic_install_code, stop_canister, update_settings, CanisterId, CanisterIdRecord, CanisterInstallMode, CanisterSettings, CreateCanisterArgument, InstallCodeArgument, UpdateSettingsArgument, }; -use ic_cdk::api::time; +use ic_cdk::api::{canister_balance128, time}; pub async fn create_canister_install_code( controllers: Vec, @@ -88,3 +90,52 @@ pub async fn segment_status(canister_id: CanisterId) -> SegmentStatusResult { Err((_, message)) => Err(["Failed to get canister status: ".to_string(), message].join("")), } } + +pub async fn deposit_cycles( + DepositCyclesArgs { + destination_id, + cycles_to_retain, + }: DepositCyclesArgs, +) -> Result<(), String> { + let balance = canister_balance128(); + + if balance < cycles_to_retain { + return Err(format!( + "Balance ({}) is lower than the amount of cycles {} to retain.", + balance, cycles_to_retain + )); + } + + let cycles = balance - cycles_to_retain; + + let result = ic_deposit_cycles( + CanisterIdRecord { + canister_id: destination_id, + }, + cycles, + ) + .await; + + match result { + Err((_, message)) => Err(["Deposit cycles failed.", &message].join(" - ")), + Ok(_) => Ok(()), + } +} + +pub async fn stop_segment(canister_id: CanisterId) -> Result<(), String> { + let result = stop_canister(CanisterIdRecord { canister_id }).await; + + match result { + Err((_, message)) => Err(["Cannot stop segment.", &message].join(" - ")), + Ok(_) => Ok(()), + } +} + +pub async fn delete_segment(canister_id: CanisterId) -> Result<(), String> { + let result = delete_canister(CanisterIdRecord { canister_id }).await; + + match result { + Err((_, message)) => Err(["Cannot delete segment.", &message].join(" - ")), + Ok(_) => Ok(()), + } +} diff --git a/src/shared/src/types.rs b/src/shared/src/types.rs index 8a566bc0f..5fb98400c 100644 --- a/src/shared/src/types.rs +++ b/src/shared/src/types.rs @@ -53,7 +53,7 @@ pub mod state { pub mod interface { use crate::types::cronjob::CronJobStatusesSegments; use crate::types::state::{ControllerId, ControllerScope, Metadata, MissionControlId, UserId}; - use candid::CandidType; + use candid::{CandidType, Principal}; use ic_ledger_types::BlockIndex; use serde::Deserialize; @@ -114,6 +114,12 @@ pub mod interface { pub satellites: CronJobStatusesSegments, pub orbiters: CronJobStatusesSegments, } + + #[derive(CandidType, Deserialize)] + pub struct DepositCyclesArgs { + pub destination_id: Principal, + pub cycles_to_retain: u128, + } } pub mod ledger {