Skip to main content

solo_lib/sdk/qcloud/
lighthouse.rs

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