Skip to content

Commit ba0050d

Browse files
committed
rust 1.0.5
1 parent 7213efa commit ba0050d

File tree

6 files changed

+290
-7
lines changed

6 files changed

+290
-7
lines changed

rust/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "xnode-deployer"
33
description = "Deploy new Xnodes on several hardware providers"
4-
version = "1.0.4"
4+
version = "1.0.5"
55
edition = "2024"
66
repository = "https://github.com/Openmesh-Network/xnode-deployer"
77
license = "MIT"
@@ -14,6 +14,7 @@ serde_json = "1.0"
1414
tokio = "1"
1515

1616
[features]
17-
default = [] # ["all"]
18-
all = ["hivelocity"]
17+
default = [] # ["full"]
18+
full = ["hivelocity", "hyperstack"]
1919
hivelocity = []
20+
hyperstack = []

rust/src/hivelocity/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ impl XnodeDeployer for HivelocityDeployer {
176176

177177
async fn ipv4(
178178
&self,
179-
xnode: Self::ProviderOutput,
179+
xnode: &Self::ProviderOutput,
180180
) -> Result<OptionalSupport<Option<Ipv4Addr>>, Error> {
181181
let device_id = xnode.device_id;
182182
let scope = match self.hardware {
@@ -209,7 +209,7 @@ impl XnodeDeployer for HivelocityDeployer {
209209
}
210210
}
211211

212-
#[derive(Debug)]
212+
#[derive(Debug, Clone)]
213213
pub struct HivelocityOutput {
214214
pub device_id: u64,
215215
}

rust/src/hyperstack/mod.rs

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
use std::{fmt::Display, net::Ipv4Addr, str::FromStr};
2+
3+
use reqwest::Client;
4+
use serde_json::json;
5+
6+
use crate::{
7+
DeployInput, Error,
8+
OptionalSupport::{self, Supported},
9+
XnodeDeployer, XnodeDeployerError,
10+
utils::XnodeDeployerErrorInner,
11+
};
12+
13+
#[derive(Debug)]
14+
pub enum HyperstackError {
15+
ResponseNotObject {
16+
response: serde_json::Value,
17+
},
18+
ResponseMissingId {
19+
map: serde_json::Map<String, serde_json::Value>,
20+
},
21+
ResponseMissingInstances {
22+
map: serde_json::Map<String, serde_json::Value>,
23+
},
24+
ResponseInvalidInstances {
25+
instances: serde_json::Value,
26+
},
27+
ResponseEmptyInstances {},
28+
ResponseInvalidId {
29+
id: serde_json::Value,
30+
},
31+
}
32+
33+
impl Display for HyperstackError {
34+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35+
f.write_str(
36+
match self {
37+
HyperstackError::ResponseNotObject { response } => {
38+
format!("Hyperstack response not object: {response}")
39+
}
40+
HyperstackError::ResponseMissingInstances { map } => {
41+
format!("Hyperstack response missing instances: {map:?}")
42+
}
43+
HyperstackError::ResponseInvalidInstances { instances } => {
44+
format!("Hyperstack response invalid instances: {instances:?}")
45+
}
46+
HyperstackError::ResponseEmptyInstances {} => {
47+
format!("Hyperstack response empty instances")
48+
}
49+
HyperstackError::ResponseMissingId { map } => {
50+
format!("Hyperstack response missing id: {map:?}")
51+
}
52+
HyperstackError::ResponseInvalidId { id } => {
53+
format!("Hyperstack response invalid id: {id}")
54+
}
55+
}
56+
.as_str(),
57+
)
58+
}
59+
}
60+
61+
pub struct HyperstackDeployer {
62+
client: Client,
63+
api_key: String,
64+
hardware: HyperstackHardware,
65+
}
66+
67+
impl HyperstackDeployer {
68+
pub fn new(api_key: String, hardware: HyperstackHardware) -> Self {
69+
Self {
70+
client: Client::new(),
71+
api_key,
72+
hardware,
73+
}
74+
}
75+
}
76+
77+
impl XnodeDeployer for HyperstackDeployer {
78+
type ProviderOutput = HyperstackOutput;
79+
80+
async fn deploy(&self, input: DeployInput) -> Result<Self::ProviderOutput, Error> {
81+
log::info!(
82+
"Hyperstack deployment of {input:?} on {hardware:?} started",
83+
hardware = self.hardware
84+
);
85+
let response = match &self.hardware {
86+
HyperstackHardware::VirtualMachine {
87+
name,
88+
environment_name,
89+
flavor_name,
90+
key_name,
91+
} => self
92+
.client
93+
.post("https://infrahub-api.nexgencloud.com/v1/core/virtual-machines")
94+
.json(&json!({
95+
"name": name,
96+
"environment_name": environment_name,
97+
"image_name": "Ubuntu Server 22.04 LTS (Jammy Jellyfish)",
98+
"flavor_name": flavor_name,
99+
"key_name": key_name,
100+
"count": 1,
101+
"assign_floating_ip": true,
102+
"user_data": input.cloud_init(),
103+
"security_rules": [
104+
{
105+
"direction": "ingress",
106+
"protocol": "tcp",
107+
"ethertype": "IPv4",
108+
"remote_ip_prefix": "0.0.0.0/0",
109+
"port_range_min": 1,
110+
"port_range_max": 65535
111+
},
112+
{
113+
"direction": "ingress",
114+
"protocol": "udp",
115+
"ethertype": "IPv4",
116+
"remote_ip_prefix": "0.0.0.0/0",
117+
"port_range_min": 1,
118+
"port_range_max": 65535
119+
}
120+
]
121+
})),
122+
}
123+
.header("api_key", self.api_key.clone())
124+
.send()
125+
.await
126+
.and_then(|response| response.error_for_status())
127+
.map_err(Error::ReqwestError)?
128+
.json::<serde_json::Value>()
129+
.await
130+
.map_err(Error::ReqwestError)?;
131+
132+
let id = match &response {
133+
serde_json::Value::Object(map) => map
134+
.get("instances")
135+
.ok_or(Error::XnodeDeployerError(XnodeDeployerError::new(
136+
XnodeDeployerErrorInner::HyperstackError(
137+
HyperstackError::ResponseMissingInstances { map: map.clone() },
138+
),
139+
)))
140+
.and_then(|instances| match instances {
141+
serde_json::Value::Array(array) => {
142+
array
143+
.first()
144+
.ok_or(Error::XnodeDeployerError(XnodeDeployerError::new(
145+
XnodeDeployerErrorInner::HyperstackError(
146+
HyperstackError::ResponseEmptyInstances {},
147+
),
148+
)))
149+
}
150+
_ => Err(Error::XnodeDeployerError(XnodeDeployerError::new(
151+
XnodeDeployerErrorInner::HyperstackError(
152+
HyperstackError::ResponseInvalidInstances {
153+
instances: instances.clone(),
154+
},
155+
),
156+
))),
157+
})
158+
.and_then(|instance| match instance {
159+
serde_json::Value::Object(map) => map
160+
.get("id")
161+
.ok_or(Error::XnodeDeployerError(XnodeDeployerError::new(
162+
XnodeDeployerErrorInner::HyperstackError(
163+
HyperstackError::ResponseMissingId { map: map.clone() },
164+
),
165+
)))
166+
.and_then(|id| {
167+
match id {
168+
serde_json::Value::Number(number) => number.as_u64(),
169+
_ => None,
170+
}
171+
.ok_or(Error::XnodeDeployerError(
172+
XnodeDeployerError::new(XnodeDeployerErrorInner::HyperstackError(
173+
HyperstackError::ResponseInvalidId { id: id.clone() },
174+
)),
175+
))
176+
}),
177+
_ => Err(Error::XnodeDeployerError(XnodeDeployerError::new(
178+
XnodeDeployerErrorInner::HyperstackError(
179+
HyperstackError::ResponseNotObject {
180+
response: response.clone(),
181+
},
182+
),
183+
))),
184+
}),
185+
_ => Err(Error::XnodeDeployerError(XnodeDeployerError::new(
186+
XnodeDeployerErrorInner::HyperstackError(HyperstackError::ResponseNotObject {
187+
response: response.clone(),
188+
}),
189+
))),
190+
};
191+
let id = match id {
192+
Ok(id) => id,
193+
Err(e) => return Err(e),
194+
};
195+
196+
let output = Self::ProviderOutput { id };
197+
log::info!("Hyperstack deployment succeeded: {output:?}");
198+
Ok(output)
199+
}
200+
201+
async fn undeploy(&self, xnode: Self::ProviderOutput) -> Option<Error> {
202+
let id = xnode.id;
203+
log::info!("Undeploying hyperstack device {id} started");
204+
if let Err(e) = self
205+
.client
206+
.delete(format!(
207+
"https://infrahub-api.nexgencloud.com/v1/core/virtual-machines/{id}"
208+
))
209+
.header("api_key", self.api_key.clone())
210+
.send()
211+
.await
212+
.and_then(|response| response.error_for_status())
213+
{
214+
return Some(Error::ReqwestError(e));
215+
}
216+
217+
log::info!("Undeploying hyperstack device {id} succeeded");
218+
None
219+
}
220+
221+
async fn ipv4(
222+
&self,
223+
xnode: &Self::ProviderOutput,
224+
) -> Result<OptionalSupport<Option<Ipv4Addr>>, Error> {
225+
let id = xnode.id;
226+
let response = self
227+
.client
228+
.get(format!(
229+
"https://infrahub-api.nexgencloud.com/v1/core/virtual-machines/{id}"
230+
))
231+
.header("api_key", self.api_key.clone())
232+
.send()
233+
.await
234+
.and_then(|response| response.error_for_status())
235+
.map_err(Error::ReqwestError)?
236+
.json::<serde_json::Value>()
237+
.await
238+
.map_err(Error::ReqwestError)?;
239+
240+
if let serde_json::Value::Object(map) = &response {
241+
if let Some(serde_json::Value::Object(instance)) = map.get("instance") {
242+
if let Some(serde_json::Value::String(floating_ip)) = instance.get("floating_ip") {
243+
if let Ok(ip) = Ipv4Addr::from_str(floating_ip) {
244+
return Ok(Supported(Some(ip)));
245+
}
246+
}
247+
}
248+
};
249+
250+
Ok(Supported(None))
251+
}
252+
}
253+
254+
#[derive(Debug, Clone)]
255+
pub struct HyperstackOutput {
256+
pub id: u64,
257+
}
258+
259+
#[derive(Debug)]
260+
pub enum HyperstackHardware {
261+
// https://docs.hyperstack.cloud/docs/api-reference/core-resources/virtual-machines/vm-core/create-vms
262+
VirtualMachine {
263+
name: String,
264+
environment_name: String,
265+
flavor_name: String,
266+
key_name: String,
267+
},
268+
}
269+
270+
#[derive(Debug)]
271+
pub enum HyperstackUndeployInput {
272+
VirtualMachine { id: u64 },
273+
}

rust/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub use utils::{Error, XnodeDeployerError};
77

88
#[cfg(feature = "hivelocity")]
99
pub mod hivelocity;
10+
#[cfg(feature = "hyperstack")]
11+
pub mod hyperstack;
1012

1113
#[derive(Serialize, Deserialize, Debug)]
1214
pub struct DeployInput {
@@ -18,6 +20,7 @@ pub struct DeployInput {
1820
pub initial_config: Option<String>,
1921
}
2022

23+
#[derive(Debug)]
2124
pub enum OptionalSupport<T> {
2225
NotSupported,
2326
Supported(T),
@@ -38,7 +41,7 @@ pub trait XnodeDeployer: Send + Sync {
3841
/// Get ipv4 address of deployed hardware
3942
fn ipv4(
4043
&self,
41-
xnode: Self::ProviderOutput,
44+
xnode: &Self::ProviderOutput,
4245
) -> impl Future<Output = Result<OptionalSupport<Option<Ipv4Addr>>, Error>> + Send;
4346
}
4447

rust/src/utils/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::fmt::Display;
22

33
#[cfg(feature = "hivelocity")]
44
use crate::hivelocity::HivelocityError;
5+
#[cfg(feature = "hyperstack")]
6+
use crate::hyperstack::HyperstackError;
57

68
#[derive(Debug)]
79
pub enum Error {
@@ -25,6 +27,8 @@ pub enum XnodeDeployerErrorInner {
2527
Default,
2628
#[cfg(feature = "hivelocity")]
2729
HivelocityError(HivelocityError),
30+
#[cfg(feature = "hyperstack")]
31+
HyperstackError(HyperstackError),
2832
}
2933

3034
impl Display for XnodeDeployerErrorInner {
@@ -34,6 +38,8 @@ impl Display for XnodeDeployerErrorInner {
3438
XnodeDeployerErrorInner::Default => "".to_string(),
3539
#[cfg(feature = "hivelocity")]
3640
XnodeDeployerErrorInner::HivelocityError(e) => e.to_string(),
41+
#[cfg(feature = "hyperstack")]
42+
XnodeDeployerErrorInner::HyperstackError(e) => e.to_string(),
3743
}
3844
.as_str(),
3945
)

0 commit comments

Comments
 (0)