feat: postgres
This commit is contained in:
parent
6bf10b093c
commit
0b8525fe05
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1351,6 +1351,16 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "example-postgres"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"harmony",
|
||||||
|
"serde",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "example-rust"
|
name = "example-rust"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -4807,6 +4817,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 1.0.4",
|
"mio 1.0.4",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
10
examples/postgres/Cargo.toml
Normal file
10
examples/postgres/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "example-postgres"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
harmony = { path = "../../harmony" }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
async-trait = "0.1.80"
|
84
examples/postgres/src/main.rs
Normal file
84
examples/postgres/src/main.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use harmony::{
|
||||||
|
data::{PostgresDatabase, PostgresUser},
|
||||||
|
interpret::InterpretError,
|
||||||
|
inventory::Inventory,
|
||||||
|
maestro::Maestro,
|
||||||
|
modules::postgres::PostgresScore,
|
||||||
|
topology::{PostgresServer, Topology},
|
||||||
|
};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct MockTopology;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Topology for MockTopology {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"MockTopology"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ensure_ready(&self) -> Result<harmony::interpret::Outcome, InterpretError> {
|
||||||
|
Ok(harmony::interpret::Outcome::new(
|
||||||
|
harmony::interpret::InterpretStatus::SUCCESS,
|
||||||
|
"Mock topology is always ready".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl PostgresServer for MockTopology {
|
||||||
|
async fn ensure_users_exist(&self, users: Vec<PostgresUser>) -> Result<(), InterpretError> {
|
||||||
|
println!("Ensuring users exist:");
|
||||||
|
for user in users {
|
||||||
|
println!(" - {}: {}", user.name, user.password);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ensure_databases_exist(
|
||||||
|
&self,
|
||||||
|
databases: Vec<PostgresDatabase>,
|
||||||
|
) -> Result<(), InterpretError> {
|
||||||
|
println!("Ensuring databases exist:");
|
||||||
|
for db in databases {
|
||||||
|
println!(" - {}: owner={}", db.name, db.owner);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let users = vec![
|
||||||
|
PostgresUser {
|
||||||
|
name: "admin".to_string(),
|
||||||
|
password: "password".to_string(),
|
||||||
|
},
|
||||||
|
PostgresUser {
|
||||||
|
name: "user".to_string(),
|
||||||
|
password: "password".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let databases = vec![
|
||||||
|
PostgresDatabase {
|
||||||
|
name: "app_db".to_string(),
|
||||||
|
owner: "admin".to_string(),
|
||||||
|
},
|
||||||
|
PostgresDatabase {
|
||||||
|
name: "user_db".to_string(),
|
||||||
|
owner: "user".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let postgres_score = PostgresScore::new(users, databases);
|
||||||
|
|
||||||
|
let inventory = Inventory::empty();
|
||||||
|
let topology = MockTopology;
|
||||||
|
let maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.interpret(Box::new(postgres_score)).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -2,3 +2,6 @@ mod id;
|
|||||||
mod version;
|
mod version;
|
||||||
pub use id::*;
|
pub use id::*;
|
||||||
pub use version::*;
|
pub use version::*;
|
||||||
|
|
||||||
|
mod postgres;
|
||||||
|
pub use postgres::*;
|
||||||
|
13
harmony/src/domain/data/postgres.rs
Normal file
13
harmony/src/domain/data/postgres.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PostgresUser {
|
||||||
|
pub name: String,
|
||||||
|
pub password: String, // In a real scenario, this should be a secret type
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PostgresDatabase {
|
||||||
|
pub name: String,
|
||||||
|
pub owner: String,
|
||||||
|
}
|
@ -22,6 +22,7 @@ pub enum InterpretName {
|
|||||||
K3dInstallation,
|
K3dInstallation,
|
||||||
TenantInterpret,
|
TenantInterpret,
|
||||||
Application,
|
Application,
|
||||||
|
Postgres,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for InterpretName {
|
impl std::fmt::Display for InterpretName {
|
||||||
@ -39,6 +40,7 @@ impl std::fmt::Display for InterpretName {
|
|||||||
InterpretName::K3dInstallation => f.write_str("K3dInstallation"),
|
InterpretName::K3dInstallation => f.write_str("K3dInstallation"),
|
||||||
InterpretName::TenantInterpret => f.write_str("Tenant"),
|
InterpretName::TenantInterpret => f.write_str("Tenant"),
|
||||||
InterpretName::Application => f.write_str("Application"),
|
InterpretName::Application => f.write_str("Application"),
|
||||||
|
InterpretName::Postgres => f.write_str("Postgres"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,9 @@ pub use network::*;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
pub use tftp::*;
|
pub use tftp::*;
|
||||||
|
|
||||||
|
mod postgres;
|
||||||
|
pub use postgres::*;
|
||||||
|
|
||||||
mod helm_command;
|
mod helm_command;
|
||||||
pub use helm_command::*;
|
pub use helm_command::*;
|
||||||
|
|
||||||
|
14
harmony/src/domain/topology/postgres.rs
Normal file
14
harmony/src/domain/topology/postgres.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use crate::{
|
||||||
|
data::{PostgresDatabase, PostgresUser},
|
||||||
|
interpret::InterpretError,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait PostgresServer {
|
||||||
|
async fn ensure_users_exist(&self, users: Vec<PostgresUser>) -> Result<(), InterpretError>;
|
||||||
|
async fn ensure_databases_exist(
|
||||||
|
&self,
|
||||||
|
databases: Vec<PostgresDatabase>,
|
||||||
|
) -> Result<(), InterpretError>;
|
||||||
|
}
|
@ -16,3 +16,4 @@ pub mod opnsense;
|
|||||||
pub mod prometheus;
|
pub mod prometheus;
|
||||||
pub mod tenant;
|
pub mod tenant;
|
||||||
pub mod tftp;
|
pub mod tftp;
|
||||||
|
pub mod postgres;
|
||||||
|
119
harmony/src/modules/postgres.rs
Normal file
119
harmony/src/modules/postgres.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use derive_new::new;
|
||||||
|
use log::info;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::{PostgresDatabase, PostgresUser, Version},
|
||||||
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
|
inventory::Inventory,
|
||||||
|
score::Score,
|
||||||
|
topology::{PostgresServer, Topology},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, new, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PostgresScore {
|
||||||
|
users: Vec<PostgresUser>,
|
||||||
|
databases: Vec<PostgresDatabase>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Topology + PostgresServer> Score<T> for PostgresScore {
|
||||||
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
|
Box::new(PostgresInterpret::new(self.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"PostgresScore".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PostgresInterpret {
|
||||||
|
score: PostgresScore,
|
||||||
|
version: Version,
|
||||||
|
status: InterpretStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PostgresInterpret {
|
||||||
|
pub fn new(score: PostgresScore) -> Self {
|
||||||
|
let version = Version::from("1.0.0").expect("Version should be valid");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
version,
|
||||||
|
score,
|
||||||
|
status: InterpretStatus::QUEUED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ensure_users_exist<P: PostgresServer>(
|
||||||
|
&self,
|
||||||
|
postgres_server: &P,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let users = &self.score.users;
|
||||||
|
postgres_server.ensure_users_exist(users.clone()).await?;
|
||||||
|
|
||||||
|
Ok(Outcome::new(
|
||||||
|
InterpretStatus::SUCCESS,
|
||||||
|
format!(
|
||||||
|
"PostgresInterpret ensured {} users exist successfully",
|
||||||
|
users.len()
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ensure_databases_exist<P: PostgresServer>(
|
||||||
|
&self,
|
||||||
|
postgres_server: &P,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let databases = &self.score.databases;
|
||||||
|
postgres_server
|
||||||
|
.ensure_databases_exist(databases.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Outcome::new(
|
||||||
|
InterpretStatus::SUCCESS,
|
||||||
|
format!(
|
||||||
|
"PostgresInterpret ensured {} databases exist successfully",
|
||||||
|
databases.len()
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: Topology + PostgresServer> Interpret<T> for PostgresInterpret {
|
||||||
|
fn get_name(&self) -> InterpretName {
|
||||||
|
InterpretName::Postgres
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version(&self) -> crate::domain::data::Version {
|
||||||
|
self.version.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_status(&self) -> InterpretStatus {
|
||||||
|
self.status.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_children(&self) -> Vec<crate::domain::data::Id> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
inventory: &Inventory,
|
||||||
|
topology: &T,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
info!(
|
||||||
|
"Executing {} on inventory {inventory:?})",
|
||||||
|
<PostgresInterpret as Interpret<T>>::get_name(self)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.ensure_users_exist(topology).await?;
|
||||||
|
self.ensure_databases_exist(topology).await?;
|
||||||
|
|
||||||
|
Ok(Outcome::new(
|
||||||
|
InterpretStatus::SUCCESS,
|
||||||
|
"Postgres Interpret execution successful".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user