misc: initial implementation

This commit is contained in:
hdbg
2025-12-04 18:30:19 +01:00
parent c20f8d6d5f
commit ba079d24b5
6 changed files with 43 additions and 51 deletions

View File

@@ -1,3 +1,3 @@
version = "18.1" version = "18.1"
password = "odqAsS49KNyaXjrw" password = "P8t42UQ53iIza2Ic"
port = 5432 port = 5432

View File

@@ -1,8 +1,7 @@
_______ _______ __ __
| || || |_| |
_ __ __ ___ __ | _ || ___|| |
| '_ \ / _` \ \/ / | |_| || | __ | |
| |_) | (_| |> < | ___|| || | | |
| .__/ \__, /_/\_\ | | | |_| || _ |
| | __/ | |___| |_______||__| |__|
|_| |___/

View File

@@ -1,4 +1,4 @@
use clap::{Args, Parser, Subcommand, builder::styling}; use clap::{Parser, Subcommand, builder::styling};
const STYLES: styling::Styles = styling::Styles::styled() const STYLES: styling::Styles = styling::Styles::styled()
.header(styling::AnsiColor::Green.on_default().bold()) .header(styling::AnsiColor::Green.on_default().bold())

View File

@@ -35,7 +35,7 @@ impl Display for PostgresVersion {
} }
} }
const PROJECT_FILENAME: &'static str = "pgx.toml"; const PROJECT_FILENAME: &str = "pgx.toml";
/// Configuration stored in pgx.toml /// Configuration stored in pgx.toml
#[serde_as] #[serde_as]

View File

@@ -8,13 +8,13 @@ use bollard::{
errors::Error, errors::Error,
query_parameters::{ query_parameters::{
CreateContainerOptions, CreateImageOptions, InspectContainerOptions, ListImagesOptions, CreateContainerOptions, CreateImageOptions, InspectContainerOptions, ListImagesOptions,
ListImagesOptionsBuilder, SearchImagesOptions, StartContainerOptions, StopContainerOptions, StartContainerOptions, StopContainerOptions,
}, },
secret::{ContainerConfig, ContainerCreateBody, CreateImageInfo}, secret::{ContainerCreateBody, CreateImageInfo},
}; };
use futures::{Stream, StreamExt, TryStreamExt}; use futures::{Stream, StreamExt, TryStreamExt};
use indicatif::{MultiProgress, ProgressBar, ProgressState, ProgressStyle}; use indicatif::{MultiProgress, ProgressBar, ProgressState, ProgressStyle};
use miette::{Context, IntoDiagnostic, Result, diagnostic}; use miette::{Context, IntoDiagnostic, Result};
use tracing::info; use tracing::info;
use crate::{ use crate::{
@@ -38,7 +38,7 @@ const DEFAULT_POSTGRES_PORT: u16 = 5432;
const PORT_SEARCH_RANGE: u16 = 100; const PORT_SEARCH_RANGE: u16 = 100;
fn format_image(ver: &PostgresVersion) -> String { fn format_image(ver: &PostgresVersion) -> String {
format!("{DOCKERHUB_POSTGRES}:{}", ver.to_string()) format!("{DOCKERHUB_POSTGRES}:{}", ver)
} }
fn find_available_port() -> Result<u16> { fn find_available_port() -> Result<u16> {
@@ -60,9 +60,7 @@ fn find_available_port() -> Result<u16> {
fn new_download_pb(multi: &MultiProgress, layer_id: &str) -> ProgressBar { fn new_download_pb(multi: &MultiProgress, layer_id: &str) -> ProgressBar {
let pb = multi.add(ProgressBar::new(0)); let pb = multi.add(ProgressBar::new(0));
pb.set_style( pb.set_style(
ProgressStyle::with_template(&format!( ProgressStyle::with_template(&"{spinner:.green} [{elapsed_precise}] {msg} [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})".to_string())
"{{spinner:.green}} [{{elapsed_precise}}] {{msg}} [{{wide_bar:.cyan/blue}}] {{bytes}}/{{total_bytes}} ({{eta}})"
))
.unwrap() .unwrap()
.with_key("eta", |state: &ProgressState, w: &mut dyn Write| { .with_key("eta", |state: &ProgressState, w: &mut dyn Write| {
write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap() write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()
@@ -399,16 +397,15 @@ const DATABASE: &str = "postgres";
const PASSWORD_LENGTH: usize = 16; const PASSWORD_LENGTH: usize = 16;
pub fn generate_password() -> String { pub fn generate_password() -> String {
let password = (&mut rand::rng()) (&mut rand::rng())
.sample_iter(Alphanumeric) .sample_iter(Alphanumeric)
.take(PASSWORD_LENGTH) .take(PASSWORD_LENGTH)
.map(|b| b as char) .map(|b| b as char)
.collect(); .collect()
password
} }
const MAX_RETRIES: u32 = 10; const MAX_RETRIES: u32 = 10;
const VERIFY_DURATION_SECS: u64 = 5; const VERIFY_DURATION_SECS: u64 = 10;
pub struct Controller { pub struct Controller {
pub docker: DockerController, pub docker: DockerController,
@@ -473,7 +470,7 @@ impl Controller {
let instance_state = state.get_mut(&project.name); let instance_state = state.get_mut(&project.name);
let container_id = match instance_state { let container_id = match instance_state {
Some(instance) => match self.ensure_container_exists(&instance).await? { Some(instance) => match self.ensure_container_exists(instance).await? {
Some(id) => id, Some(id) => id,
None => self.update_project_container(project, &mut state).await?, None => self.update_project_container(project, &mut state).await?,
}, },
@@ -493,7 +490,6 @@ impl Controller {
.is_container_running_by_id(&container_id) .is_container_running_by_id(&container_id)
.await? .await?
{ {
println!("Container is already running");
return Ok(()); return Ok(());
} }
@@ -503,7 +499,7 @@ impl Controller {
let result = self.try_starting_container(&container_id, attempt).await; let result = self.try_starting_container(&container_id, attempt).await;
match result { match result {
Ok(_) => break, Ok(_) => return Ok(()),
Err(err) => println!("Error: {:#?}", err), Err(err) => println!("Error: {:#?}", err),
} }
@@ -520,11 +516,9 @@ impl Controller {
container_id: &String, container_id: &String,
attempt: u32, attempt: u32,
) -> Result<(), miette::Error> { ) -> Result<(), miette::Error> {
Ok(
match self.docker.start_container_by_id(container_id).await { match self.docker.start_container_by_id(container_id).await {
Ok(_) => { Ok(_) => {
tokio::time::sleep(tokio::time::Duration::from_secs(VERIFY_DURATION_SECS)) tokio::time::sleep(tokio::time::Duration::from_secs(VERIFY_DURATION_SECS)).await;
.await;
if self.docker.is_container_running_by_id(container_id).await? { if self.docker.is_container_running_by_id(container_id).await? {
println!("Container started successfully and verified running"); println!("Container started successfully and verified running");
@@ -543,8 +537,8 @@ impl Controller {
attempt, MAX_RETRIES, e attempt, MAX_RETRIES, e
); );
} }
}, };
) Ok(())
} }
async fn update_project_container( async fn update_project_container(
@@ -596,7 +590,7 @@ impl Controller {
_container_id: &String, _container_id: &String,
container_version: PostgresVersion, container_version: PostgresVersion,
) -> Result<(), miette::Error> { ) -> Result<(), miette::Error> {
Ok(if container_version != project.config.version { let _: () = if container_version != project.config.version {
let needs_upgrade = container_version < project.config.version; let needs_upgrade = container_version < project.config.version;
if needs_upgrade { if needs_upgrade {
@@ -627,6 +621,7 @@ impl Controller {
project.config.version project.config.version
); );
} }
}) };
Ok(())
} }
} }

View File

@@ -14,12 +14,12 @@ use crate::controller::Controller;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
println!("{}", include_str!("./banner.txt")); println!("{}", include_str!("./banner.txt"));
let controller = Controller::new().await?;
let cli = Cli::parse(); let cli = Cli::parse();
init_tracing(cli.verbose); init_tracing(cli.verbose);
info!("pgx.start"); info!("pgx.start");
let controller = Controller::new().await?;
match cli.command { match cli.command {
cli::Commands::Init => controller.init_project().await?, cli::Commands::Init => controller.init_project().await?,
@@ -33,7 +33,5 @@ async fn main() -> Result<()> {
fn init_tracing(verbose: bool) { fn init_tracing(verbose: bool) {
use tracing_subscriber::{fmt, prelude::*}; use tracing_subscriber::{fmt, prelude::*};
tracing_subscriber::registry() tracing_subscriber::fmt::init();
.with(fmt::layer().with_target(false).with_level(true))
.init();
} }