Compare commits
1 Commits
master
...
dev/postgr
Author | SHA1 | Date | |
---|---|---|---|
|
0b8525fe05 |
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1351,6 +1351,16 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example-postgres"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"harmony",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example-rust"
|
||||
version = "0.1.0"
|
||||
@ -4807,6 +4817,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio 1.0.4",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"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;
|
||||
pub use id::*;
|
||||
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,
|
||||
TenantInterpret,
|
||||
Application,
|
||||
Postgres,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InterpretName {
|
||||
@ -39,6 +40,7 @@ impl std::fmt::Display for InterpretName {
|
||||
InterpretName::K3dInstallation => f.write_str("K3dInstallation"),
|
||||
InterpretName::TenantInterpret => f.write_str("Tenant"),
|
||||
InterpretName::Application => f.write_str("Application"),
|
||||
InterpretName::Postgres => f.write_str("Postgres"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ pub use network::*;
|
||||
use serde::Serialize;
|
||||
pub use tftp::*;
|
||||
|
||||
mod postgres;
|
||||
pub use postgres::*;
|
||||
|
||||
mod 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 tenant;
|
||||
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