1use crate::{
4 cdk::types::Cycles,
5 ids::{ComponentInstanceId, FleetSubnetCanisterPoolConfig},
6};
7use candid::{CandidType, Principal};
8use serde::{Deserialize, Serialize};
9
10#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
12pub struct CanisterPoolClaim {
13 pub component: Option<ComponentInstanceId>,
14 pub operation_id: [u8; 32],
15}
16
17#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
19pub enum CanisterPoolAssetOrigin {
20 Created,
21 Imported,
22 Recycled,
23}
24
25#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
27pub enum CanisterPoolAssetStatus {
28 PendingReset,
29 Ready,
30 Claimed { claim: CanisterPoolClaim },
31 HandingOff { recipient: Principal },
32 Failed { reason: String },
33}
34
35#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
37pub struct CanisterPoolAsset {
38 pub canister_id: Principal,
39 pub cycles: Cycles,
40 pub origin: CanisterPoolAssetOrigin,
41 pub status: CanisterPoolAssetStatus,
42 pub added_at_ns: u64,
43 pub updated_at_ns: u64,
44}
45
46#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
48pub struct CanisterPoolCreation {
49 pub operation_id: [u8; 32],
50 pub canister_cycles: Cycles,
51 pub canister_id: Option<Principal>,
52 pub prepared_at_ns: u64,
53}
54
55#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
57pub struct CanisterPoolHandoff {
58 pub canister_id: Principal,
59 pub recipient: Principal,
60 pub prepared_at_ns: u64,
61}
62
63#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
65pub struct CanisterPoolStatusRequest {
66 pub start_after: Option<Principal>,
67 pub limit: u16,
68}
69
70#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
72pub struct CanisterPoolResponse {
73 pub config: FleetSubnetCanisterPoolConfig,
74 pub tracked: u32,
75 pub surplus: u32,
76 pub ready: u32,
77 pub pending_reset: u32,
78 pub claimed: u32,
79 pub handing_off: u32,
80 pub failed: u32,
81 pub completed_handoffs: u64,
82 pub pending_creation: Option<CanisterPoolCreation>,
83 pub pending_handoff: Option<CanisterPoolHandoff>,
84 pub entries: Vec<CanisterPoolAsset>,
85 pub next_start_after: Option<Principal>,
86}
87
88#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
90pub enum PoolAdminCommand {
91 Maintain,
92 Import {
93 canister_id: Principal,
94 },
95 RetryReset {
96 canister_id: Principal,
97 },
98 Handoff {
99 canister_id: Principal,
100 recipient: Principal,
101 },
102}
103
104#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
106pub enum PoolAdminResponse {
107 Maintained,
108 MaintenancePaused {
109 reason: String,
110 },
111 Created {
112 canister_id: Principal,
113 },
114 Imported {
115 canister_id: Principal,
116 },
117 ResetQueued {
118 canister_id: Principal,
119 },
120 ResetReady {
121 canister_id: Principal,
122 },
123 HandedOff {
124 canister_id: Principal,
125 recipient: Principal,
126 },
127 ResetFailed {
128 canister_id: Principal,
129 reason: String,
130 },
131}
132
133#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn pool_status_and_admin_contracts_round_trip_through_candid() {
143 let canister_id = Principal::from_slice(&[7; 29]);
144 let response = CanisterPoolResponse {
145 config: FleetSubnetCanisterPoolConfig {
146 minimum_size: 3,
147 maximum_size: 10,
148 canister_cycles: Cycles::new(5_000_000_000_000),
149 },
150 tracked: 1,
151 surplus: 0,
152 ready: 0,
153 pending_reset: 0,
154 claimed: 0,
155 handing_off: 0,
156 failed: 1,
157 completed_handoffs: 0,
158 pending_creation: None,
159 pending_handoff: None,
160 entries: vec![CanisterPoolAsset {
161 canister_id,
162 cycles: Cycles::new(4_000_000_000_000),
163 origin: CanisterPoolAssetOrigin::Recycled,
164 status: CanisterPoolAssetStatus::Failed {
165 reason: "below configured cycles".to_string(),
166 },
167 added_at_ns: 10,
168 updated_at_ns: 11,
169 }],
170 next_start_after: None,
171 };
172 let bytes = candid::encode_one(&response).expect("encode pool status");
173 assert_eq!(
174 candid::decode_one::<CanisterPoolResponse>(&bytes).expect("decode pool status"),
175 response,
176 );
177
178 let command = PoolAdminCommand::Import { canister_id };
179 let bytes = candid::encode_one(&command).expect("encode pool command");
180 assert_eq!(
181 candid::decode_one::<PoolAdminCommand>(&bytes).expect("decode pool command"),
182 command,
183 );
184 }
185}