mirror of
https://github.com/moghtech/komodo.git
synced 2026-09-10 00:01:02 +00:00
start on hetzner
This commit is contained in:
@@ -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,
|
||||
}
|
||||
@@ -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,4 +1,5 @@
|
||||
pub mod aws;
|
||||
pub mod hetzner;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BuildCleanupData {
|
||||
|
||||
Reference in New Issue
Block a user