summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-07-10 11:05:51 -0400
committerCara Salter <cara@devcara.com>2022-07-10 11:05:51 -0400
commitd3279088e3a816db2c254b957159d5b697dc0f62 (patch)
tree764557ef69bdff984faad4885c8e00d6c9546107
parent131dd5ab9df295b0bad320675b2307afcbf7d076 (diff)
downloadhomeworld-d3279088e3a816db2c254b957159d5b697dc0f62.tar.gz
homeworld-d3279088e3a816db2c254b957159d5b697dc0f62.zip
colonies: Fully support {meta, user}-data
cloud-init should be able to use this as a seed to properly set up the Planet.
-rw-r--r--Cargo.lock28
-rw-r--r--Cargo.toml1
-rw-r--r--migrations/20220710145536_planet_metadata.sql5
-rw-r--r--src/errors.rs9
-rw-r--r--src/handlers/colonies.rs55
-rw-r--r--src/handlers/ships.rs8
-rw-r--r--src/main.rs6
7 files changed, 103 insertions, 9 deletions
diff --git a/Cargo.lock b/Cargo.lock
index adda6e1..748e68a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -525,6 +525,7 @@ dependencies = [
"hyper",
"kankyo",
"serde",
+ "serde_yaml",
"solarlib",
"sqlx",
"thiserror",
@@ -678,6 +679,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
name = "lock_api"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1165,6 +1172,18 @@ dependencies = [
]
[[package]]
+name = "serde_yaml"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec0091e1f5aa338283ce049bd9dfefd55e1f168ac233e85c1ffe0038fb48cbe"
+dependencies = [
+ "indexmap",
+ "ryu",
+ "serde",
+ "yaml-rust",
+]
+
+[[package]]
name = "sha-1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1906,3 +1925,12 @@ name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 33e2464..e4972f3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ kankyo = "0.3"
# Middleware
axum-auth = "0.2"
+serde_yaml = "0.8.25"
[dependencies.solarlib]
git = "https://git.carathe.dev/solard/solarlib"
diff --git a/migrations/20220710145536_planet_metadata.sql b/migrations/20220710145536_planet_metadata.sql
new file mode 100644
index 0000000..8c52e33
--- /dev/null
+++ b/migrations/20220710145536_planet_metadata.sql
@@ -0,0 +1,5 @@
+-- Add migration script here
+CREATE TABLE planet_metadata (
+ uuid TEXT PRIMARY KEY,
+ hostname TEXT NOT NULL
+);
diff --git a/src/errors.rs b/src/errors.rs
index 6b3195f..e513343 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -16,6 +16,9 @@ pub enum ServiceError {
#[error("SQL error: {0}")]
Sql(#[from] sqlx::Error),
+ #[error("Generic: {0}")]
+ Generic(#[from] Box<dyn std::error::Error>),
+
#[error("Not Found")]
NotFound,
@@ -23,10 +26,14 @@ pub enum ServiceError {
NotAuthorized,
}
-pub type StringResult<T = &'static str> = Result<T, ServiceError>;
+pub type StringResult<T = String> = Result<T, ServiceError>;
+
+pub type StrResult<T= &'static str> = Result<T, ServiceError>;
pub type JsonResult<T> = Result<T, ServiceError>;
+pub type NoneResult = Result<(), ServiceError>;
+
impl IntoResponse for ServiceError {
fn into_response(self) -> Response {
let body = body::boxed(body::Full::from(self.to_string()));
diff --git a/src/handlers/colonies.rs b/src/handlers/colonies.rs
index 37ec65a..cc9ee0e 100644
--- a/src/handlers/colonies.rs
+++ b/src/handlers/colonies.rs
@@ -1,15 +1,18 @@
use std::sync::Arc;
-use axum::{Extension, Json};
-use sqlx::query_as;
+use axum::{Extension, Json, extract::Path};
+use axum_auth::AuthBearer;
+use sqlx::{query_as, query};
use crate::{
- errors::{JsonResult, ServiceError, StringResult},
+ errors::{JsonResult, ServiceError, StringResult, NoneResult},
State,
};
use solarlib::colony::{User, UserData};
+use super::ships::check_bearer;
+
#[derive(Debug, Clone)]
pub struct DbUser {
pub id: i32,
@@ -18,6 +21,12 @@ pub struct DbUser {
pub ssh_authorized_keys: Vec<String>,
}
+#[derive(Debug)]
+struct DbMeta {
+ pub uuid: String,
+ pub hostname: String,
+}
+
impl From<DbUser> for User {
fn from(o: DbUser) -> Self {
Self {
@@ -44,3 +53,43 @@ pub async fn list_users(state: Extension<Arc<State>>) -> JsonResult<Json<UserDat
Ok(Json(data))
}
+
+pub async fn create_user(Json(new_user): Json<User>, state: Extension<Arc<State>>, AuthBearer(token): AuthBearer) -> NoneResult {
+ check_bearer(token)?;
+ let mut conn = state.conn.acquire().await?;
+
+ query!("INSERT INTO seed_users (name, groups, ssh_authorized_keys) VALUES ($1, $2, $3)", new_user.name, &new_user.groups, &new_user.ssh_authorized_keys)
+ .execute(&mut conn)
+ .await?;
+
+ Ok(())
+}
+
+pub async fn add_metadata(Path((uuid, hostname)): Path<(String, String)>, state: Extension<Arc<State>>, AuthBearer(token): AuthBearer) -> NoneResult {
+ check_bearer(token)?;
+ let mut conn = state.conn.acquire().await?;
+
+ query!("INSERT INTO planet_metadata (uuid, hostname) VALUES ($1, $2)", uuid, hostname).execute(&mut conn).await?;
+ Ok(())
+}
+
+pub async fn meta_data(Path(uuid): Path<String>, state: Extension<Arc<State>>) -> StringResult {
+ let mut conn = state.conn.acquire().await?;
+
+ let m = query_as!(DbMeta, "SELECT * FROM planet_metadata WHERE uuid=$1", uuid).fetch_one(&mut conn).await?;
+
+ Ok(format!("instance-id: {}\nlocal-hostname: {}", m.uuid, m.hostname))
+}
+
+pub async fn user_data(Path(_uuid): Path<String>, state: Extension<Arc<State>>) -> StringResult {
+ let mut conn = state.conn.acquire().await?;
+
+ let db_users = query_as!(DbUser, "SELECT * FROM seed_users").fetch_all(&mut conn).await?;
+
+ let users = db_users.into_iter().map(|u| u.into()).collect::<Vec<User>>();
+
+ let data = UserData { users };
+
+ Ok(format!("#cloud-config\n{}", serde_yaml::to_string(&data).unwrap()))
+
+}
diff --git a/src/handlers/ships.rs b/src/handlers/ships.rs
index 91944fc..3101e54 100644
--- a/src/handlers/ships.rs
+++ b/src/handlers/ships.rs
@@ -8,7 +8,7 @@ use sqlx::{query, query_as, Error as SqlxError};
use tracing::log::warn;
use crate::{
- errors::{JsonResult, ServiceError, StringResult},
+ errors::{JsonResult, ServiceError, StringResult, StrResult},
State,
};
@@ -31,7 +31,7 @@ pub async fn new(
Json(new_ship): Json<Ship>,
state: Extension<Arc<State>>,
AuthBearer(token): AuthBearer,
-) -> StringResult {
+) -> StrResult {
check_bearer(token)?;
let mut conn = state.conn.acquire().await?;
@@ -52,7 +52,7 @@ pub async fn delete(
Path(shasum): Path<Sha256>,
state: Extension<Arc<State>>,
AuthBearer(token): AuthBearer,
-) -> StringResult {
+) -> StrResult {
check_bearer(token)?;
let mut conn = state.conn.acquire().await?;
@@ -89,7 +89,7 @@ pub async fn get(
Ok(Json(db_ship))
}
-fn check_bearer(token: String) -> Result<(), ServiceError> {
+pub fn check_bearer(token: String) -> Result<(), ServiceError> {
let expected_token = match std::env::var("SHARED_KEY") {
Ok(t) => t,
Err(_) => {
diff --git a/src/main.rs b/src/main.rs
index 67b54ea..0fffe82 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -35,7 +35,7 @@ async fn main() {
.with(tracing_subscriber::fmt::layer())
.init();
- let mut conn = PgPoolOptions::new()
+ let conn = PgPoolOptions::new()
.max_connections(5)
.connect(
&std::env::var("DATABASE_URL")
@@ -55,6 +55,10 @@ async fn main() {
.route("/ships/delete/:shasum", delete(handlers::ships::delete))
.route("/ships/get/:shasum", get(handlers::ships::get))
.route("/users/list", get(handlers::colonies::list_users))
+ .route("/users/add", post(handlers::colonies::create_user))
+ .route("/:uuid/user-data", get(handlers::colonies::user_data))
+ .route("/meta/:uuid/:hostname", post(handlers::colonies::add_metadata))
+ .route("/:uuid/meta-data", get(handlers::colonies::meta_data))
.layer(
ServiceBuilder::new()
.layer(HandleErrorLayer::new(|error: BoxError| async move {