start on hetzner

This commit is contained in:
mbecker20
2024-05-23 01:47:02 -07:00
parent d008c95853
commit ec47bb11ee
4 changed files with 213 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerServer {
pub id: i64,
pub image: HetznerImage,
pub name: String,
pub primary_disk_size: f64,
pub private_net: Vec<HetznerPrivateNet>,
pub public_net: HetznerPublicNet,
pub server_type: HetznerServerType,
pub status: String,
pub volumes: Vec<i64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerServerType {
pub architecture: String,
pub cores: i64,
pub cpu_type: String,
pub description: String,
pub disk: f64,
pub id: i64,
pub memory: f64,
pub name: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerPrivateNet {
pub alias_ips: Vec<String>,
pub ip: String,
pub mac_address: String,
pub network: i64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerPublicNet {
pub firewalls: Vec<HetznerFirewall>,
pub ipv4: HetznerIpv4,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerFirewall {
pub id: i64,
pub status: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerIpv4 {
pub blocked: bool,
pub dns_prt: String,
pub ip: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerImage {
pub description: String,
pub name: String,
pub os_flavor: String,
pub os_version: String,
pub rapid_deploy: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerAction {
pub command: String,
pub error: HetznerError,
pub finished: Option<String>,
pub id: i64,
pub progress: i32,
pub resources: Vec<HetznerResource>,
pub started: String,
pub status: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerError {
pub code: String,
pub message: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HetznerResource {
pub id: i64,
#[serde(rename = "type")]
pub ty: String,
}
@@ -0,0 +1,65 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::common::{HetznerAction, HetznerServer};
#[derive(Debug, Clone, Serialize)]
pub struct CreateServerBody {
/// Auto-mount Volumes after attach
pub automount: bool,
/// ID or name of Datacenter to create Server in (must not be used together with location)
pub datacenter: Option<String>,
/// ID or name of Location to create Server in (must not be used together with datacenter)
pub location: Option<String>,
/// Firewalls which should be applied on the Server's public network interface at creation time
pub firewalls: Vec<Firewall>,
/// ID or name of the Image the Server is created from
pub image: String,
/// User-defined labels (key-value pairs) for the Resource
pub labels: HashMap<String, String>,
/// Name of the Server to create (must be unique per Project and a valid hostname as per RFC 1123)
pub name: String,
/// Network IDs which should be attached to the Server private network interface at the creation time
pub networks: Vec<i64>,
/// ID of the Placement Group the server should be in
pub placement_group: i64,
/// Public Network options
pub public_net: PublicNet,
/// ID or name of the Server type this Server should be created with
pub server_type: String,
/// SSH key IDs ( integer ) or names ( string ) which should be injected into the Server at creation time
pub ssh_keys: Vec<String>,
/// This automatically triggers a Power on a Server-Server Action after the creation is finished and is returned in the next_actions response object.
pub start_after_create: bool,
/// Cloud-Init user data to use during Server creation. This field is limited to 32KiB.
pub user_data: String,
/// Volume IDs which should be attached to the Server at the creation time. Volumes must be in the same Location.
pub volumes: Vec<i64>,
}
#[derive(Debug, Clone, Copy, Serialize)]
pub struct Firewall {
/// ID of the Firewall
pub firewall: i64,
}
#[derive(Debug, Clone, Copy, Serialize)]
pub struct PublicNet {
/// Attach an IPv4 on the public NIC. If false, no IPv4 address will be attached.
pub enable_ipv4: bool,
/// Attach an IPv6 on the public NIC. If false, no IPv6 address will be attached.
pub enable_ipv6: bool,
/// ID of the ipv4 Primary IP to use. If omitted and enable_ipv4 is true, a new ipv4 Primary IP will automatically be created.
pub ipv4: Option<i64>,
/// ID of the ipv6 Primary IP to use. If omitted and enable_ipv6 is true, a new ipv6 Primary IP will automatically be created.
pub ipv6: Option<i64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CreateServerResponse {
pub action: HetznerAction,
pub next_actions: Vec<HetznerAction>,
pub root_password: Option<String>,
pub server: HetznerServer,
}
+60
View File
@@ -0,0 +1,60 @@
use std::sync::OnceLock;
use anyhow::{anyhow, Context};
use reqwest::StatusCode;
use serde::{de::DeserializeOwned, Serialize};
use self::create_server::{CreateServerBody, CreateServerResponse};
pub mod common;
pub mod create_server;
const BASE_URL: &str = "https://api.hetzner.cloud/v1";
pub struct HetznerClient {
client: reqwest::Client,
token: String,
}
impl HetznerClient {
fn new(token: &str) -> HetznerClient {
HetznerClient {
client: Default::default(),
token: format!("Bearer {token}"),
}
}
pub async fn create_server(
&self,
body: &CreateServerBody,
) -> anyhow::Result<CreateServerResponse> {
self.post("/servers", body).await
}
async fn post<Body: Serialize, Res: DeserializeOwned>(
&self,
path: &str,
body: &Body,
) -> anyhow::Result<Res> {
let res = self
.client
.post(format!("{BASE_URL}{path}"))
.json(&body)
.header("authorization", &self.token)
.send()
.await
.context("failed to make request to Hetzner")?;
let status = res.status();
if status == StatusCode::OK {
res.json().await.context("failed to parse response to json")
} else {
let text = res
.text()
.await
.context("failed to get response body as text")?;
Err(anyhow!("FAILED | {path} | {status} | {text}"))
}
}
}
+1
View File
@@ -1,4 +1,5 @@
pub mod aws;
pub mod hetzner;
#[derive(Debug)]
pub enum BuildCleanupData {