Skip to main content

oci_rust_sdk/core/
mod.rs

1//! Core Services Module
2//!
3//! Contains models for Oracle Cloud Infrastructure Core Services:
4//! - Compute (instances, images, shapes)
5//! - VirtualNetwork (VCNs, subnets, security lists)
6//! - Blockstorage (volumes, backups)
7
8pub mod client;
9pub mod error;
10pub mod models;
11pub mod region;
12pub mod requests;
13pub mod responses;
14pub mod retry;
15
16// Re-exports for convenience
17pub use error::{OciError, Result};
18pub use models::*;
19pub use region::Region;
20pub use requests::*;
21pub use responses::*;
22pub use retry::{Retrier, RetryConfiguration};
23
24use crate::auth::provider::AuthProvider;
25use client::http_client::OciClient;
26use std::sync::Arc;
27
28/// Client configuration for core services
29pub struct ClientConfig {
30    pub auth_provider: Arc<dyn AuthProvider>,
31    pub region: Region,
32    pub timeout: std::time::Duration,
33    pub retry: Retrier,
34}
35
36/// Create a new core services client
37pub fn client(config: ClientConfig) -> Result<CoreClient> {
38    let endpoint = format!("https://iaas.{}.oraclecloud.com", config.region.id());
39    let client = OciClient::with_timeout(config.auth_provider, endpoint, config.timeout)?.with_retrier(config.retry);
40
41    Ok(CoreClient { client })
42}
43
44#[async_trait::async_trait]
45pub trait CoreApi: Send + Sync {
46    async fn list_instances(
47        &self,
48        request: ListInstancesRequest,
49    ) -> Result<ListInstancesResponse>;
50
51    async fn launch_instance(
52        &self,
53        request: LaunchInstanceRequest,
54    ) -> Result<LaunchInstanceResponse>;
55
56    async fn get_instance(&self, request: GetInstanceRequest) -> Result<GetInstanceResponse>;
57
58    async fn terminate_instance(
59        &self,
60        request: TerminateInstanceRequest,
61    ) -> Result<TerminateInstanceResponse>;
62
63    async fn list_vnic_attachments(
64        &self,
65        request: ListVnicAttachmentsRequest,
66    ) -> Result<ListVnicAttachmentsResponse>;
67
68    async fn get_vnic(&self, request: GetVnicRequest) -> Result<GetVnicResponse>;
69
70    async fn list_public_ips(
71        &self,
72        request: ListPublicIpsRequest,
73    ) -> Result<ListPublicIpsResponse>;
74}
75
76/// Core Services API client
77pub struct CoreClient {
78    client: OciClient,
79}
80
81#[async_trait::async_trait]
82impl CoreApi for CoreClient {
83    async fn list_instances(
84        &self,
85        request: ListInstancesRequest,
86    ) -> Result<ListInstancesResponse> {
87        let mut query_params = vec![("compartmentId".to_string(), request.compartment_id.clone())];
88
89        if let Some(av_domain) = &request.availability_domain {
90            query_params.push(("availabilityDomain".to_string(), av_domain.clone()));
91        }
92        if let Some(display_name) = &request.display_name {
93            query_params.push(("displayName".to_string(), display_name.clone()));
94        }
95        if let Some(limit) = request.limit {
96            query_params.push(("limit".to_string(), limit.to_string()));
97        }
98        if let Some(page) = &request.page {
99            query_params.push(("page".to_string(), page.clone()));
100        }
101        if let Some(lifecycle_state) = &request.lifecycle_state {
102            query_params.push(("lifecycleState".to_string(), lifecycle_state.clone()));
103        }
104
105        let query_string = if query_params.is_empty() {
106            String::new()
107        } else {
108            format!(
109                "?{}",
110                query_params
111                    .iter()
112                    .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
113                    .collect::<Vec<_>>()
114                    .join("&")
115            )
116        };
117
118        let path = format!("/20160918/instances{}", query_string);
119        let response = self.client.get(&path).await?;
120
121        Ok(ListInstancesResponse {
122            opc_next_page: response.get_header("opc-next-page"),
123            opc_request_id: response.get_header("opc-request-id"),
124            items: response.body,
125        })
126    }
127
128    async fn launch_instance(
129        &self,
130        request: LaunchInstanceRequest,
131    ) -> Result<LaunchInstanceResponse> {
132        let path = "/20160918/instances".to_string();
133        let response = self
134            .client
135            .post(&path, Some(&request.launch_instance_details))
136            .await?;
137
138        Ok(LaunchInstanceResponse {
139            etag: response.get_header("etag"),
140            opc_request_id: response.get_header("opc-request-id"),
141            opc_work_request_id: response.get_header("opc-work-request-id"),
142            instance: response.body,
143        })
144    }
145
146    async fn get_instance(&self, request: GetInstanceRequest) -> Result<GetInstanceResponse> {
147        let path = format!("/20160918/instances/{}", request.instance_id);
148        let response = self.client.get(&path).await?;
149
150        Ok(GetInstanceResponse {
151            etag: response.get_header("etag"),
152            opc_request_id: response.get_header("opc-request-id"),
153            instance: response.body,
154        })
155    }
156
157    async fn terminate_instance(
158        &self,
159        request: TerminateInstanceRequest,
160    ) -> Result<TerminateInstanceResponse> {
161        let mut query_params = Vec::new();
162        if let Some(preserve_boot_volume) = request.preserve_boot_volume {
163            query_params.push(("preserveBootVolume".to_string(), preserve_boot_volume.to_string()));
164        }
165
166        let query_string = if query_params.is_empty() {
167            String::new()
168        } else {
169            format!(
170                "?{}",
171                query_params
172                    .iter()
173                    .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
174                    .collect::<Vec<_>>()
175                    .join("&")
176            )
177        };
178
179        let path = format!("/20160918/instances/{}{}", request.instance_id, query_string);
180        let response: client::http_client::OciResponse<serde_json::Value> =
181            self.client.delete(&path).await?;
182
183        Ok(TerminateInstanceResponse {
184            opc_request_id: response.get_header("opc-request-id").unwrap_or_default(),
185        })
186    }
187
188    async fn list_vnic_attachments(
189        &self,
190        request: ListVnicAttachmentsRequest,
191    ) -> Result<ListVnicAttachmentsResponse> {
192        let mut query_params = vec![("compartmentId".to_string(), request.compartment_id.clone())];
193
194        if let Some(instance_id) = &request.instance_id {
195            query_params.push(("instanceId".to_string(), instance_id.clone()));
196        }
197        if let Some(limit) = request.limit {
198            query_params.push(("limit".to_string(), limit.to_string()));
199        }
200        if let Some(page) = &request.page {
201            query_params.push(("page".to_string(), page.clone()));
202        }
203
204        let query_string = format!(
205            "?{}",
206            query_params
207                .iter()
208                .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
209                .collect::<Vec<_>>()
210                .join("&")
211        );
212
213        let path = format!("/20160918/vnicAttachments{}", query_string);
214        let response = self.client.get(&path).await?;
215
216        Ok(ListVnicAttachmentsResponse {
217            opc_next_page: response.get_header("opc-next-page").unwrap_or_default(),
218            opc_request_id: response.get_header("opc-request-id").unwrap_or_default(),
219            items: response.body,
220        })
221    }
222
223    async fn get_vnic(&self, request: GetVnicRequest) -> Result<GetVnicResponse> {
224        let path = format!("/20160918/vnics/{}", request.vnic_id);
225        let response = self.client.get(&path).await?;
226
227        Ok(GetVnicResponse {
228            etag: response.get_header("etag").unwrap_or_default(),
229            opc_request_id: response.get_header("opc-request-id").unwrap_or_default(),
230            vnic: response.body,
231        })
232    }
233
234    async fn list_public_ips(
235        &self,
236        request: ListPublicIpsRequest,
237    ) -> Result<ListPublicIpsResponse> {
238        let mut query_params = vec![("scope".to_string(), format!("{:?}", request.scope))];
239        query_params.push(("compartmentId".to_string(), request.compartment_id.clone()));
240
241        if let Some(limit) = request.limit {
242            query_params.push(("limit".to_string(), limit.to_string()));
243        }
244        if let Some(page) = &request.page {
245            query_params.push(("page".to_string(), page.clone()));
246        }
247        if let Some(availability_domain) = &request.availability_domain {
248            query_params.push((
249                "availabilityDomain".to_string(),
250                availability_domain.clone(),
251            ));
252        }
253        if let Some(lifetime) = &request.lifetime {
254            query_params.push(("lifetime".to_string(), format!("{:?}", lifetime)));
255        }
256
257        let query_string = format!(
258            "?{}",
259            query_params
260                .iter()
261                .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
262                .collect::<Vec<_>>()
263                .join("&")
264        );
265
266        let path = format!("/20160918/publicIps{}", query_string);
267        let response = self.client.get(&path).await?;
268
269        Ok(ListPublicIpsResponse {
270            opc_next_page: response.get_header("opc-next-page"),
271            opc_request_id: response.get_header("opc-request-id"),
272            items: response.body,
273        })
274    }
275}