solo_lib/sdk/qcloud/
lighthouse.rs

1//! # Qcloud Lighthouse
2//!
3//! Begin with the [`go`] function
4
5use std::{borrow::Borrow, result::Result::Ok};
6
7use anyhow::Result;
8use hashbrown::HashSet;
9use reqwest::Client;
10use serde::{Deserialize, Serialize};
11
12use super::util::{
13    BasicRequest, CommonResponse, Empty, MachineType, Secret, parse_response,
14    request_builder,
15};
16
17/// Lighthouse instance
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Instance {
20    pub id: String,
21    pub region: String,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct FirewallRuleInfo {
26    #[serde(rename = "AppType", skip_serializing_if = "Option::is_none")]
27    pub app_type: Option<String>,
28
29    #[serde(rename = "Protocol")]
30    pub protocol: String,
31
32    #[serde(rename = "Port")]
33    pub port: String,
34
35    #[serde(rename = "CidrBlock")]
36    pub cidr_block: String,
37
38    #[serde(rename = "Action")]
39    pub action: String,
40
41    #[serde(rename = "FirewallRuleDescription")]
42    pub firewall_rule_description: String,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46struct DescribeFirewallRulesRequest {
47    #[serde(rename = "InstanceId")]
48    instance_id: String,
49    #[serde(rename = "Offset")]
50    offset: i32,
51    #[serde(rename = "Limit")]
52    limit: i32,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct DescribeFirewallRulesResponse {
57    #[serde(rename = "FirewallRuleSet")]
58    pub firewall_rule_set: Vec<FirewallRuleInfo>,
59    #[serde(rename = "FirewallVersion")]
60    pub firewall_version: i32,
61    #[serde(rename = "TotalCount")]
62    pub total_count: i32,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66struct ModifyFirewallRulesRequest {
67    #[serde(rename = "InstanceId")]
68    instance_id: String,
69    #[serde(rename = "FirewallRules")]
70    firewall_rules: Vec<FirewallRuleInfo>,
71}
72
73/// ### Solo GO! - Main function
74///
75/// Start to modify the firewall rules.
76///
77/// This function is a basic implementation of the SDK. It is recommended to use
78/// this function directly.
79///
80/// ### Example
81/// ```rust
82/// use solo_lib::sdk::qcloud::{
83///     Secret,
84///     lighthouse::{Instance, go},
85/// };
86///
87/// #[tokio::main]
88/// async fn main() {
89///     let client = solo_lib::client::new();
90///     let secret = Secret {
91///         secret_id: "secret_id".to_string(),
92///         secret_key: "secret_key".to_string(),
93///     };
94///     let instance = Instance {
95///         id: "instance_id".to_string(),
96///         region: "instance_region".to_string(),
97///     };
98///     let _result = go(
99///         &client,
100///         &instance,
101///         &secret,
102///         "current_ipv4",
103///         &[
104///             "firewall_rule_one".to_string(),
105///             "firewall_rule_two".to_string(),
106///         ],
107///     )
108///     .await
109///     .unwrap();
110/// }
111/// ```
112pub async fn go(
113    client: &Client,
114    instance: impl Borrow<Instance>,
115    secret: impl Borrow<Secret>,
116    current_ip: &str,
117    matched_descriptions: &[String],
118) -> Result<()> {
119    let response =
120        list_rules(client, instance.borrow(), secret.borrow()).await?;
121    let firewall_rules = &response.response.data.firewall_rule_set;
122    let (firewall_rules, require_update) =
123        compare_rules(firewall_rules, current_ip, matched_descriptions);
124    if require_update {
125        modify_rules(
126            client,
127            instance.borrow(),
128            secret.borrow(),
129            &firewall_rules,
130        )
131        .await?;
132    }
133    Ok(())
134}
135
136/// ### SDK Implementation DescribeFirewallRules
137///
138/// Note that this function is a single step of solo. Use it only if you
139/// would like to hook.
140pub async fn list_rules(
141    client: &Client,
142    instance: impl Borrow<Instance>,
143    secret: impl Borrow<Secret>,
144) -> Result<CommonResponse<DescribeFirewallRulesResponse>> {
145    let instance = instance.borrow();
146
147    let request = DescribeFirewallRulesRequest {
148        instance_id: instance.id.clone(),
149        offset: 0,
150        limit: 100,
151    };
152    let payload = serde_json::to_string(&request)?;
153    let basic_request = BasicRequest {
154        machine_type: MachineType::Lighthouse,
155        action: "DescribeFirewallRules",
156        payload,
157        region: instance.region.clone(),
158        secret: secret.borrow(),
159    };
160    let request = request_builder(client, basic_request)?;
161    let result = client.execute(request).await?;
162    let result = result.text().await?;
163
164    parse_response::<CommonResponse<DescribeFirewallRulesResponse>>(&result)
165}
166
167/// ### SDK Process CompareRules
168///
169/// Note that this function is a single step of solo. Use it only if you
170/// would like to hook.
171pub fn compare_rules(
172    firewall_rules: &[FirewallRuleInfo],
173    current_ip: &str,
174    matched_descriptions: &[String],
175) -> (Vec<FirewallRuleInfo>, bool) {
176    let matched_set: HashSet<&str> =
177        matched_descriptions.iter().map(|s| s.as_str()).collect();
178
179    let mut require_update = false;
180    let modified_rules: Vec<FirewallRuleInfo> = firewall_rules
181        .iter()
182        .map(|rule| {
183            if matched_set.contains(rule.firewall_rule_description.as_str()) {
184                if rule.cidr_block != current_ip {
185                    require_update = true;
186                    FirewallRuleInfo {
187                        cidr_block: current_ip.into(),
188                        ..rule.clone()
189                    }
190                } else {
191                    rule.clone()
192                }
193            } else {
194                rule.clone()
195            }
196        })
197        .collect();
198
199    (modified_rules, require_update)
200}
201
202/// ### SDK Implementation ModifyFirewallRules
203///
204/// Note that this function is a single step of solo. Use it only if you
205/// would like to hook.
206pub async fn modify_rules(
207    client: &Client,
208    instance: impl Borrow<Instance>,
209    secret: impl Borrow<Secret>,
210    firewall_rules: &[FirewallRuleInfo],
211) -> Result<CommonResponse<Empty>> {
212    let instance = instance.borrow();
213
214    let firewall_rules: Vec<FirewallRuleInfo> = firewall_rules
215        .iter()
216        .cloned()
217        .map(|mut rule| {
218            rule.app_type = None;
219            rule
220        })
221        .collect();
222
223    let request = ModifyFirewallRulesRequest {
224        instance_id: instance.id.clone(),
225        firewall_rules,
226    };
227
228    let payload = serde_json::to_string(&request)?;
229    let basic_request = BasicRequest {
230        machine_type: MachineType::Lighthouse,
231        action: "ModifyFirewallRules",
232        payload,
233        region: instance.region.clone(),
234        secret: secret.borrow(),
235    };
236
237    let request = request_builder(client, basic_request)?;
238    let result = client.execute(request).await?;
239    let result = result.text().await?;
240
241    parse_response::<CommonResponse<Empty>>(&result)
242}