Skip to main content

ic_query/cloud_engine/
mod.rs

1//! CloudEngine control-plane and official Dashboard report models, adapters, and renderers.
2
3#[cfg(feature = "cloud-engine-host")]
4mod build;
5#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
6mod list;
7mod model;
8mod node;
9mod provider;
10#[cfg(feature = "cloud-engine-host")]
11mod source;
12mod text;
13#[cfg(feature = "cloud-engine-host")]
14mod wire;
15
16#[cfg(feature = "cloud-engine-host")]
17use thiserror::Error as ThisError;
18
19#[cfg(feature = "cloud-engine-host")]
20pub use build::{
21    build_cloud_engine_operator_report, build_cloud_engine_operator_report_with_source,
22    build_cloud_engine_prices_report, build_cloud_engine_prices_report_with_source,
23};
24#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
25pub use list::{
26    build_cloud_engine_list_report, build_cloud_engine_list_report_with_progress,
27    build_cloud_engine_list_report_with_sources,
28    build_cloud_engine_list_report_with_sources_and_progress,
29};
30#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
31pub use model::{CloudEngineListReport, CloudEngineListRow};
32pub use model::{
33    CloudEngineNodeType, CloudEngineOperatorReport, CloudEnginePriceRow, CloudEnginePricesReport,
34    CloudEngineReportContext,
35};
36#[cfg(feature = "cloud-engine-host")]
37pub use model::{
38    CloudEngineOperatorBindingSourceData, CloudEngineOperatorSourceData,
39    CloudEnginePricesSourceData,
40};
41pub use node::{
42    CLOUD_ENGINE_NODE_INCLUDED_STATUSES, CLOUD_ENGINE_NODE_REWARD_TYPE, CloudEngineNodeInfoReport,
43    CloudEngineNodeInfoRequest, CloudEngineNodeListReport, CloudEngineNodeListRequest,
44    CloudEngineNodeRow, MAX_CLOUD_ENGINE_NODE_ROWS, cloud_engine_node_info_report_text,
45    cloud_engine_node_list_report_text,
46};
47#[cfg(feature = "dashboard-host")]
48pub use node::{
49    CloudEngineNodeInfoSourceData, CloudEngineNodeListSourceData, CloudEngineNodeSource,
50    build_cloud_engine_node_info_report, build_cloud_engine_node_info_report_with_source,
51    build_cloud_engine_node_list_report, build_cloud_engine_node_list_report_with_source,
52};
53pub use provider::{
54    CloudEngineProviderInfoReport, CloudEngineProviderInfoRequest, CloudEngineProviderListReport,
55    CloudEngineProviderListRequest, CloudEngineProviderLocation, CloudEngineProviderRow,
56    MAX_CLOUD_ENGINE_PROVIDER_LOCATIONS, MAX_CLOUD_ENGINE_PROVIDER_SOURCE_ROWS,
57    cloud_engine_provider_info_report_text, cloud_engine_provider_list_report_text,
58};
59#[cfg(feature = "dashboard-host")]
60pub use provider::{
61    CloudEngineProviderInfoSourceData, CloudEngineProviderListSourceData,
62    CloudEngineProviderSource, build_cloud_engine_provider_info_report,
63    build_cloud_engine_provider_info_report_with_source, build_cloud_engine_provider_list_report,
64    build_cloud_engine_provider_list_report_with_source,
65};
66#[cfg(feature = "cloud-engine-host")]
67pub use source::{CloudEngineOperatorBindingSource, CloudEngineSource, LiveCloudEngineSource};
68#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
69pub use text::cloud_engine_list_report_text;
70pub use text::{cloud_engine_operator_report_text, cloud_engine_prices_report_text};
71
72/// Mainnet CloudEngine control-plane registry canister principal.
73pub const MAINNET_CLOUD_ENGINE_CANISTER_ID: &str = "q6cfj-fyaaa-aaaar-qb77q-cai";
74
75/// Default replica endpoint used for live CloudEngine control-plane queries.
76pub const DEFAULT_CLOUD_ENGINE_SOURCE_ENDPOINT: &str = "https://icp-api.io";
77
78/// Default official Dashboard endpoint for CloudEngine provider and Type4 node reports.
79pub const DEFAULT_CLOUD_ENGINE_DASHBOARD_SOURCE_ENDPOINT: &str =
80    "https://ic-api.internetcomputer.org/api/v3";
81
82/// Maximum marketplace rows accepted from one live response.
83pub const MAX_CLOUD_ENGINE_PRICE_ROWS: usize = 1_000;
84
85/// Maximum Registry CloudEngine Subnets followed by one list invocation.
86pub const MAX_CLOUD_ENGINE_LIST_ROWS: usize = 100;
87
88/// Maximum claimed domains accepted from one engine operator.
89pub const MAX_CLOUD_ENGINE_DOMAINS: usize = 100;
90
91/// Maximum decimal digits accepted for one marketplace cycle amount.
92pub const MAX_CLOUD_ENGINE_CYCLE_DECIMAL_DIGITS: usize = 256;
93
94#[cfg(feature = "cloud-engine-host")]
95const CLOUD_ENGINE_REPORT_SCHEMA_VERSION: u32 = 1;
96#[cfg(feature = "cloud-engine-host")]
97const CLOUD_ENGINE_AUTHORITY: &str = "cloud_engine_control_plane_canister";
98
99///
100/// CloudEngineHostError
101///
102/// Failure while collecting or validating one live CloudEngine report.
103///
104
105#[cfg(feature = "cloud-engine-host")]
106#[derive(Debug, ThisError)]
107pub enum CloudEngineHostError {
108    /// The requested network is not the supported mainnet identity.
109    #[error(
110        "`icq cloud-engine` supports only the mainnet `ic` network\n\nThese reports query the mainnet CloudEngine control-plane canister.\n\nTry:\n  icq --network ic cloud-engine prices"
111    )]
112    UnsupportedNetwork {
113        /// Rejected network identity.
114        network: String,
115    },
116
117    /// A requested principal is invalid.
118    #[error("invalid {field}: {reason}")]
119    InvalidPrincipal {
120        /// Principal field being parsed.
121        field: &'static str,
122        /// Principal parsing failure.
123        reason: String,
124    },
125
126    /// The IC agent could not be constructed for the requested endpoint.
127    #[error("failed to build IC agent for {endpoint}: {reason}")]
128    AgentBuild {
129        /// Endpoint used to build the agent.
130        endpoint: String,
131        /// Agent construction failure.
132        reason: String,
133    },
134
135    /// The built-in CloudEngine control-plane principal could not be parsed.
136    #[error("invalid built-in CloudEngine canister principal: {reason}")]
137    CanisterId {
138        /// Principal parsing failure.
139        reason: String,
140    },
141
142    /// A CloudEngine control-plane query failed.
143    #[error("CloudEngine agent call {method} failed: {reason}")]
144    AgentCall {
145        /// Canister method being queried.
146        method: &'static str,
147        /// Agent call failure.
148        reason: String,
149    },
150
151    /// A CloudEngine query argument could not be Candid encoded.
152    #[error("failed to encode Candid {message}: {reason}")]
153    CandidEncode {
154        /// Candid request type.
155        message: &'static str,
156        /// Encoding failure.
157        reason: String,
158    },
159
160    /// A CloudEngine response could not be Candid decoded.
161    #[error("failed to decode Candid {message}: {reason}")]
162    CandidDecode {
163        /// Candid response type.
164        message: &'static str,
165        /// Decoding failure.
166        reason: String,
167    },
168
169    /// A custom CloudEngine source returned inconsistent or excessive data.
170    #[error("invalid CloudEngine source data: {reason}")]
171    InvalidSourceData {
172        /// Deterministic source-contract failure.
173        reason: String,
174    },
175
176    /// The Registry-backed Subnet Catalog could not supply the list inventory.
177    #[cfg(feature = "subnet-catalog-host")]
178    #[error(transparent)]
179    SubnetCatalog(#[from] crate::subnet_catalog::SubnetCatalogHostError),
180}
181
182#[cfg(feature = "cloud-engine-host")]
183fn enforce_mainnet_network(network: &str) -> Result<(), CloudEngineHostError> {
184    crate::network::enforce_mainnet_network_with(network, |network| {
185        CloudEngineHostError::UnsupportedNetwork { network }
186    })
187}
188
189#[cfg(all(test, feature = "cloud-engine-host"))]
190mod tests;