PostgresManager

Struct PostgresManager 

Source
pub struct PostgresManager { /* private fields */ }
Expand description

PostgreSQL Manager

Main entry point for managing PostgreSQL installations on remote servers.

Use PostgresManagerBuilder to construct instances.

§Example

use lmrc_postgres::{PostgresConfig, PostgresManager};

let config = PostgresConfig::builder()
    .version("15")
    .database_name("myapp")
    .username("myuser")
    .password("secure_password")
    .build()?;

let manager = PostgresManager::builder()
    .config(config)
    .server_ip("192.168.1.100")
    .ssh_user("root")
    .build()?;

manager.install().await?;

Implementations§

Source§

impl PostgresManager

Source

pub fn builder() -> PostgresManagerBuilder

Create a new builder

Source

pub async fn is_installed(&self) -> Result<bool>

Check if PostgreSQL is installed

§Example
if !manager.is_installed().await? {
    println!("PostgreSQL is not installed");
}
Source

pub async fn get_installed_version(&self) -> Result<Option<String>>

Get installed PostgreSQL version

Returns None if PostgreSQL is not installed.

Source

pub async fn install(&self) -> Result<()>

Install PostgreSQL (idempotent)

This operation is idempotent - it can be safely run multiple times. If PostgreSQL is already installed with the correct version, it will skip installation.

§Example
manager.install().await?;
println!("PostgreSQL installed successfully");
Source

pub async fn uninstall(&self, purge: bool) -> Result<()>

Uninstall PostgreSQL

§Arguments
  • purge - If true, remove all data and configuration files
§Example
// Remove PostgreSQL but keep data
manager.uninstall(false).await?;

// Remove PostgreSQL and all data
manager.uninstall(true).await?;
Source

pub async fn configure_database(&self) -> Result<()>

Configure database and user (idempotent)

Creates the database and user if they don’t exist, grants permissions. This operation is idempotent.

§Example
manager.configure_database().await?;
Source

pub async fn configure_server(&self) -> Result<()>

Configure PostgreSQL server settings

Updates server configuration (listen addresses, port, memory settings, etc.) and restarts the service.

§Example
manager.configure_server().await?;
Source

pub async fn configure(&self) -> Result<()>

Configure both database and server

Convenience method that calls both configure_database() and configure_server().

§Example
manager.configure().await?;
Source

pub async fn setup(&self) -> Result<()>

Complete setup (install and configure)

Installs PostgreSQL (if not already installed) and configures both database and server. All operations are idempotent.

§Example
manager.setup().await?;
println!("PostgreSQL is ready!");
Source

pub async fn diff(&self) -> Result<ConfigDiff>

Detect configuration differences

Compares the desired configuration with the current server configuration and returns a diff of changes.

§Example
let diff = manager.diff().await?;

if diff.has_changes() {
    println!("Configuration changes:");
    for change in diff.changes() {
        println!("  {}", change);
    }
}
Source

pub async fn apply_diff(&self, diff: &ConfigDiff) -> Result<()>

Apply configuration diff

Applies the changes detected by diff() to the server.

§Arguments
  • diff - Configuration diff to apply
§Example
let diff = manager.diff().await?;

if diff.has_changes() {
    manager.apply_diff(&diff).await?;
    println!("Configuration updated");
}
Source

pub async fn test_connection(&self) -> Result<()>

Test database connection

Attempts to connect to the database and verify credentials.

§Example
manager.test_connection().await?;
println!("Connection successful!");
Source

pub fn config(&self) -> &PostgresConfig

Get the PostgreSQL configuration

Source

pub fn server_ip(&self) -> &str

Get the server IP address

Source

pub fn ssh_user(&self) -> &str

Get the SSH username

Source

pub fn private_ip(&self) -> Option<&str>

Get the private IP (if set)

Source

pub async fn detect_platform(&self) -> Result<Platform>

Detect the server platform

§Example
let platform = manager.detect_platform().await?;
println!("Platform: {:?}", platform);
Source

pub async fn get_system_info(&self) -> Result<SystemInfo>

Get system information

§Example
let info = manager.get_system_info().await?;
println!("RAM: {}MB, Disk: {}MB, CPUs: {}",
         info.total_ram_mb, info.free_disk_mb, info.cpu_cores);
Source

pub async fn check_requirements( &self, requirements: Option<SystemRequirements>, ) -> Result<SystemInfo>

Check system requirements before installation

§Arguments
  • requirements - Optional custom requirements (uses defaults if None)
§Example
// Use default requirements
manager.check_requirements(None).await?;

// Or use custom requirements
let requirements = SystemRequirements {
    min_ram_mb: 2048,
    min_disk_mb: 10240,
    min_cpu_cores: 2,
};
manager.check_requirements(Some(requirements)).await?;
Source

pub async fn check_version_available(&self, version: &str) -> Result<bool>

Check if a PostgreSQL version is available for installation

§Example
if manager.check_version_available("16").await? {
    println!("PostgreSQL 16 is available");
}
Source

pub async fn verify_installation(&self) -> Result<()>

Verify installation comprehensively

Performs extensive verification including:

  • Binary existence
  • Version check
  • Service status
  • Configuration files
  • Network listening
§Example
manager.verify_installation().await?;
println!("Installation verified!");
Source

pub async fn upgrade(&self, new_version: &str) -> Result<()>

Upgrade PostgreSQL to a new version

§Arguments
  • new_version - Target PostgreSQL version
§Example
// Upgrade from current version to version 16
manager.upgrade("16").await?;
§Warning

This is a simplified upgrade. For production systems, you should:

  • Backup your data first
  • Review migration notes for the new version
  • Test in a staging environment
  • Plan for data migration with pg_upgrade
Source

pub async fn backup_config(&self) -> Result<ConfigBackup>

Create a backup of the current PostgreSQL configuration

Backs up:

  • postgresql.conf
  • pg_hba.conf
  • pg_ident.conf (if exists)

Returns backup metadata including backup directory and timestamp.

§Example
let backup = manager.backup_config().await?;
println!("Backup created: {}", backup.backup_dir);
Source

pub async fn list_backups(&self) -> Result<Vec<ConfigBackup>>

List available configuration backups

§Example
let backups = manager.list_backups().await?;
for backup in backups {
    println!("Backup: {} ({} files)", backup.timestamp, backup.files.len());
}
Source

pub async fn restore_backup(&self, backup: &ConfigBackup) -> Result<()>

Restore configuration from a specific backup

§Arguments
  • backup - The backup to restore
§Example
let backups = manager.list_backups().await?;
if let Some(backup) = backups.first() {
    manager.restore_backup(backup).await?;
    println!("Configuration restored from {}", backup.timestamp);
}
Source

pub async fn rollback_config(&self) -> Result<()>

Rollback to the most recent configuration backup

§Example
// Make some changes that you want to undo
// ...

// Rollback to previous configuration
manager.rollback_config().await?;
println!("Configuration rolled back successfully");
Source

pub async fn cleanup_old_backups(&self, keep_count: usize) -> Result<usize>

Clean up old configuration backups

Keeps only the most recent N backups and deletes the rest.

§Arguments
  • keep_count - Number of backups to keep
§Example
// Keep only the 5 most recent backups
let deleted = manager.cleanup_old_backups(5).await?;
println!("Deleted {} old backup(s)", deleted);
Source

pub async fn read_pg_hba(&self) -> Result<String>

Detect pg_hba.conf configuration differences

Compares the current pg_hba.conf rules with a desired state.

§Example
let current_content = manager.read_pg_hba().await?;
println!("Current pg_hba.conf:\n{}", current_content);
Source

pub async fn dry_run_configure(&self) -> Result<ConfigDiff>

Preview configuration changes without applying them (dry-run)

This method shows what changes would be made without actually applying them. Useful for validating changes before execution.

§Example
// Preview changes
let diff = manager.dry_run_configure().await?;

if diff.has_changes() {
    println!("Would make the following changes:");
    println!("{}", diff);

    // If changes look good, apply them
    manager.apply_diff(&diff).await?;
}
Source

pub async fn apply_diff_safe(&self, diff: &ConfigDiff) -> Result<()>

Apply configuration changes with automatic backup

This method:

  1. Creates a backup of the current configuration
  2. Applies the changes
  3. Verifies the changes
  4. Rolls back if verification fails
§Arguments
  • diff - Configuration diff to apply
§Example
let diff = manager.diff().await?;

if diff.has_changes() {
    // Apply with automatic backup and rollback on failure
    manager.apply_diff_safe(&diff).await?;
}
Source

pub async fn list_users(&self) -> Result<Vec<UserInfo>>

List all PostgreSQL users

§Example
let users = manager.list_users().await?;
for user in users {
    println!("User: {} (superuser: {})", user.name, user.is_superuser);
}
Source

pub async fn list_databases(&self) -> Result<Vec<DatabaseInfo>>

List all PostgreSQL databases

§Example
let databases = manager.list_databases().await?;
for db in databases {
    println!("Database: {} (owner: {}, encoding: {})", db.name, db.owner, db.encoding);
}
Source

pub async fn drop_database(&self, database_name: &str) -> Result<()>

Drop a database

§Arguments
  • database_name - Name of the database to drop
§Example
manager.drop_database("old_database").await?;
Source

pub async fn drop_user(&self, username: &str) -> Result<()>

Drop a user

§Arguments
  • username - Name of the user to drop
§Example
manager.drop_user("old_user").await?;
Source

pub async fn update_user_password( &self, username: &str, new_password: &str, ) -> Result<()>

Update a user’s password

§Arguments
  • username - Name of the user
  • new_password - New password for the user
§Example
manager.update_user_password("myuser", "new_secure_password").await?;
Source

pub async fn grant_privileges( &self, database: &str, username: &str, privileges: &[Privilege], ) -> Result<()>

Grant privileges to a user on a database

§Arguments
  • database - Database name
  • username - User to grant privileges to
  • privileges - List of privileges to grant
§Example
manager.grant_privileges(
    "myapp_db",
    "app_user",
    &[Privilege::Select, Privilege::Insert, Privilege::Update]
).await?;
Source

pub async fn revoke_privileges( &self, database: &str, username: &str, privileges: &[Privilege], ) -> Result<()>

Revoke privileges from a user on a database

§Arguments
  • database - Database name
  • username - User to revoke privileges from
  • privileges - List of privileges to revoke
§Example
manager.revoke_privileges(
    "myapp_db",
    "app_user",
    &[Privilege::Delete, Privilege::Truncate]
).await?;
Source

pub async fn create_role( &self, role_name: &str, can_login: bool, is_superuser: bool, ) -> Result<()>

Create a role

§Arguments
  • role_name - Name of the role to create
  • can_login - Whether the role can login
  • is_superuser - Whether the role is a superuser
§Example
// Create a non-login role for grouping privileges
manager.create_role("app_readonly", false, false).await?;
Source

pub async fn grant_role(&self, role_name: &str, username: &str) -> Result<()>

Grant a role to a user

§Arguments
  • role_name - Name of the role to grant
  • username - User to grant the role to
§Example
manager.grant_role("app_readonly", "new_user").await?;
Source

pub async fn revoke_role(&self, role_name: &str, username: &str) -> Result<()>

Revoke a role from a user

§Arguments
  • role_name - Name of the role to revoke
  • username - User to revoke the role from
§Example
manager.revoke_role("app_readonly", "old_user").await?;
Source

pub async fn user_exists(&self, username: &str) -> Result<bool>

Check if a user exists

§Arguments
  • username - Name of the user to check
§Example
if manager.user_exists("myuser").await? {
    println!("User exists");
}
Source

pub async fn database_exists(&self, database: &str) -> Result<bool>

Check if a database exists

§Arguments
  • database - Name of the database to check
§Example
if manager.database_exists("mydb").await? {
    println!("Database exists");
}
Source

pub async fn create_database_with_options( &self, database_name: &str, owner: Option<&str>, encoding: Option<&str>, template: Option<&str>, ) -> Result<()>

Create a database with advanced options

§Arguments
  • database_name - Name of the database to create
  • owner - Optional owner of the database
  • encoding - Optional character encoding (e.g., “UTF8”)
  • template - Optional template database
§Example
manager.create_database_with_options(
    "analytics_db",
    Some("analytics_user"),
    Some("UTF8"),
    Some("template0")
).await?;
Source

pub async fn create_user_with_options( &self, username: &str, password: &str, is_superuser: bool, can_create_db: bool, can_create_role: bool, connection_limit: Option<i32>, ) -> Result<()>

Create a user with advanced options

§Arguments
  • username - Name of the user to create
  • password - Password for the user
  • is_superuser - Whether the user should be a superuser
  • can_create_db - Whether the user can create databases
  • can_create_role - Whether the user can create roles
  • connection_limit - Optional connection limit (-1 for unlimited)
§Example
manager.create_user_with_options(
    "limited_user",
    "secure_password",
    false,
    false,
    false,
    Some(10)
).await?;

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more