From d3279088e3a816db2c254b957159d5b697dc0f62 Mon Sep 17 00:00:00 2001 From: Cara Salter Date: Sun, 10 Jul 2022 11:05:51 -0400 Subject: colonies: Fully support {meta, user}-data cloud-init should be able to use this as a seed to properly set up the Planet. --- Cargo.lock | 28 ++++++++++++++ Cargo.toml | 1 + migrations/20220710145536_planet_metadata.sql | 5 +++ src/errors.rs | 9 ++++- src/handlers/colonies.rs | 55 +++++++++++++++++++++++++-- src/handlers/ships.rs | 8 ++-- src/main.rs | 6 ++- 7 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 migrations/20220710145536_planet_metadata.sql 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", @@ -677,6 +678,12 @@ version = "0.2.126" 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" @@ -1164,6 +1171,18 @@ dependencies = [ "serde", ] +[[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" @@ -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), + #[error("Not Found")] NotFound, @@ -23,10 +26,14 @@ pub enum ServiceError { NotAuthorized, } -pub type StringResult = Result; +pub type StringResult = Result; + +pub type StrResult = Result; pub type JsonResult = Result; +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, } +#[derive(Debug)] +struct DbMeta { + pub uuid: String, + pub hostname: String, +} + impl From for User { fn from(o: DbUser) -> Self { Self { @@ -44,3 +53,43 @@ pub async fn list_users(state: Extension>) -> JsonResult, state: Extension>, 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>, 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, state: Extension>) -> 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, state: Extension>) -> 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::>(); + + 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, state: Extension>, 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, state: Extension>, 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 { -- cgit v1.2.3