diff --git a/backend/src/connector/mod.rs b/backend/src/connector/mod.rs index 36c19e3..3d3a032 100644 --- a/backend/src/connector/mod.rs +++ b/backend/src/connector/mod.rs @@ -9,14 +9,20 @@ use crate::{config::EnvConfig, connector::docker::DockerConnector}; pub mod docker; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Copy)] pub enum ConnectorType { Docker, } #[derive(FromRequestParts, Clone)] #[from_request(via(Extension))] -pub struct PlatformConnection(Arc); +pub struct PlatformConnection(Arc, ConnectorType); + +impl PlatformConnection { + pub fn typ(&self) -> ConnectorType { + self.1 + } +} router_extension!( async fn connector(self, config: &EnvConfig) -> Self { @@ -43,7 +49,7 @@ impl ConnectorType { ConnectorType::Docker => Arc::new(DockerConnector::new()?), }; - Ok(PlatformConnection(connector)) + Ok(PlatformConnection(connector, *self)) } } diff --git a/backend/src/deployment/postgres.rs b/backend/src/deployment/postgres.rs index f66362e..1170320 100644 --- a/backend/src/deployment/postgres.rs +++ b/backend/src/deployment/postgres.rs @@ -1,28 +1,135 @@ +use std::collections::HashMap; + use axum::{ Json, Router, extract::FromRequest, routing::{delete, get, post}, }; use centaurus::error::Result; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::connector::{ - Deployment, DeploymentInfo, DeploymentType, PlatformConnection, StorageOptions, + ConnectorType, Deployment, DeploymentInfo, DeploymentType, PlatformConnection, StorageOptions, }; pub fn router() -> Router { Router::new() + .route("/info", get(system_info)) .route("/", post(create_deployment)) .route("/", get(list_deployments)) .route("/", delete(remove_deployment)) } +#[derive(Serialize)] +struct SystemInfo { + connector: ConnectorType, + namespaces: Vec, +} + +async fn system_info() -> Result> { + let info = SystemInfo { + connector: ConnectorType::Docker, + namespaces: vec![], + }; + + Ok(Json(info)) +} + #[derive(FromRequest, Deserialize)] #[from_request(via(Json))] struct CreateDeployment { name: String, - storage_mb: u64, + namespace: Option, + version: PostgresVersion, + replicas: u32, + resources: DeploymentResources, + backup: DeploymentBackup, + connection: DeploymentConnection, + monitoring: DeploymentMonitoring, + advanced: DeploymentAdvanced, +} + +#[derive(Deserialize)] +struct DeploymentResources { + storage_mb: Option, + memory_request_mb: u64, + memory_limit_mb: u64, + cpu_request_millicores: u64, + cpu_limit_millicores: u64, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case", tag = "enabled")] +enum DeploymentBackup { + Disabled, + Enabled { + location: String, + schedule: String, + retention_days: u32, + }, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case", tag = "ssl_enabled")] +enum DeploymentConnection { + Disabled { + external_access: bool, + }, + Enabled { + external_access: bool, + ssl_cert: SslFile, + ssl_key: SslFile, + ssl_ca: SslCa, + }, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case", tag = "ca_enabled")] +enum SslCa { + Disabled, + Enabled { ssl_ca: SslFile }, +} + +#[derive(Deserialize)] +#[serde(tag = "type")] +enum SslFile { + Auto, + Text { content: String }, + Reference { ref_name: String, ref_key: String }, + HostFilePath { host_file_path: String }, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case", tag = "enabled")] +enum DeploymentMonitoring { + Disabled, + Enabled { + external_access: bool, + deploy_monitoring: bool, + }, +} + +#[derive(Deserialize)] +struct DeploymentAdvanced { + allow_alter_system: bool, + extra_params: HashMap, +} + +#[derive(Deserialize)] +enum PostgresVersion { + #[serde(rename = "13")] + V13, + #[serde(rename = "14")] + V14, + #[serde(rename = "15")] + V15, + #[serde(rename = "16")] + V16, + #[serde(rename = "17")] + V17, + #[serde(rename = "18")] + V18, } async fn create_deployment(conn: PlatformConnection, payload: CreateDeployment) -> Result<()> { @@ -31,7 +138,7 @@ async fn create_deployment(conn: PlatformConnection, payload: CreateDeployment) uuid: Uuid::new_v4(), typ: DeploymentType::Postgres, storage: StorageOptions { - size_mb: payload.storage_mb, + size_mb: payload.resources.storage_mb.unwrap_or(1000), }, }; diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..eccbadf --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,81 @@ +# Deployment + +## Configuration Options + +General Options: + +- Name: The display name for the deployment. +- Namespace: The Kubernetes namespace where the deployment will be created. (only Kubernetes) +- Database Version: The version of postgres to deploy. +- Replicas: The number of instances to deploy. + +Resource Options: + +- Storage Size: The amount of storage allocated for the deployment per instance (in MB). (only Kubernetes) +- Storage Class: The storage class to use for the deployment. (only Kubernetes) +- CPU Requests: The amount of CPU requested for each instance (in millicores). +- CPU Limits: The maximum amount of CPU allowed for each instance (in millicores). +- Memory Requests: The amount of memory requested for each instance (in MB). +- Memory Limits: The maximum amount of memory allowed for each instance (in MB). + +Backup Options: + +- Backup Name: The name of the backup configuration. +- Backup Schedule: The cron schedule for automatic backups. +- Backup Retention: The number of backups to retain. +- Backup Location: The location where backups will be stored. + +Connection Options: + +- External Access: Enable or disable external access to the deployment. +- SSL Configuration: Configure SSL settings for secure connections. + +Monitoring Options: + +- Toggle: Enable or disable monitoring for the deployment. +- Allow external access: Enable or disable external access to the monitoring endpoints. +- Deploy monitoring resources: Choose whether to deploy monitoring resources alongside the database deployment. (only Kubernetes) + +Advanced Options: + +- Allow Alter System: Enable or disable the ability to use ALTER SYSTEM commands. +- Extra Database Engine Parameters: Additional parameters to configure the database engine. + +Hardcoded Parameters (required for replication): + +- wal_level = replica +- hot_standby = on +- max_wal_senders = replica count \* 1.1 (primary only) +- max_replication_slots = replica count \* 1.1 (primary only) +- primary_conninfo = use environment variables (replica only) +- primary_slot_name = some name (replica only) +- sync_replication_slots = true (replica only) + +storage ref: +docker: - host file path (must exist) - file upload +kubernetes: - file upload - config map ref - secret ref + +! +ssl +ssl_ca_file (storage ref) +ssl_cert_file (storage ref) +ssl_key_file (storage ref) +wal_level = replica (restart) +allow_alter_system (default to off) + +primary: +hot_standby = on (restart) + +sending: +max_wal_senders = replica count + small number like 10% (restart) +max_replication_slots = replica count + small number like 10% (restart) + +standby: +primary_conninfo (use env) +primary_slot_name (just some name) +sync_replication_slots = true +hot_standby = on (restart) + +stats TODO (replication lag monitoring) +archiving for replication TODO +failover TODO diff --git a/frontend/package.json b/frontend/package.json index daba268..ac0587f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.7", "bits-ui": "^2.11.0", - "positron-components": "1.10.0", + "positron-components": "1.12.1", "svelte": "5.39.9", "svelte-check": "4.3.2", "tailwind-merge": "^3.3.1", diff --git a/frontend/src/app.css b/frontend/src/app.css index 6ccb162..1d90476 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -3,7 +3,7 @@ @custom-variant dark (&:is(.dark *)); -@source '../../node_modules/positron-components'; +@source '../node_modules/positron-components'; /* The default border color has changed to `currentColor` in Tailwind CSS v4, diff --git a/frontend/src/lib/backend/postgres.svelte.ts b/frontend/src/lib/backend/postgres.svelte.ts index a26fd13..5ebf25e 100644 --- a/frontend/src/lib/backend/postgres.svelte.ts +++ b/frontend/src/lib/backend/postgres.svelte.ts @@ -1,8 +1,92 @@ import { delete_, get, post, ResponseType } from 'positron-components/backend'; +import type { + CertSource, + PostgresVersion +} from '../../routes/deployments/postgres/create/schema.svelte'; export interface CreateDeployment { name: string; - storage_mb: number; + namespace?: string; + version: PostgresVersion; + replicas: number; + resources: CreateDeploymentResources; + backup: CreateDeploymentBackup; + connection: CreateDeploymentConnection; + monitoring: CreateDeploymentMonitoring; + advanced: CreateDeploymentAdvanced; +} + +export interface CreateDeploymentResources { + storage_mb?: number; + memory_request_mb: number; + memory_limit_mb: number; + cpu_request_millicores: number; + cpu_limit_millicores: number; +} + +export type CreateDeploymentBackup = + | { + enabled: 'disabled'; + } + | { + enabled: 'enabled'; + schedule: string; + retention_days: number; + storage_location: string; + }; + +export type CreateDeploymentConnection = { + external_access: boolean; +} & ( + | { + ssl_enabled: 'disabled'; + } + | { + ssl_enabled: 'enabled'; + ssl_cert: SslFile; + ssl_key: SslFile; + ssl_ca: + | { + ca_enabled: 'disabled'; + } + | { + ca_enabled: 'enabled'; + ssl_ca: SslFile; + }; + } +); + +export type SslFile = + | { + type: CertSource.Auto; + } + | { + type: CertSource.Text; + content: string; + } + | { + type: CertSource.Reference; + ref_name: string; + ref_key: string; + } + | { + type: CertSource.HostFilePath; + host_file_path: string; + }; + +export type CreateDeploymentMonitoring = + | { + enabled: 'disabled'; + } + | { + enabled: 'enabled'; + external_access: boolean; + deploy_monitoring: boolean; + }; + +export interface CreateDeploymentAdvanced { + allow_alter_system: boolean; + extra_params: Record; } export const create_deployment = async (payload: CreateDeployment) => { @@ -40,3 +124,25 @@ export const delete_deployment = async (uuid: string) => { return res; }; + +export enum ConnectorType { + Docker = 'Docker', + Kubernetes = 'Kubernetes' +} + +export interface SystemInfo { + connector: ConnectorType; + namespaces: string[]; + backup_locations: string[]; +} + +export const system_info = async () => { + let res = await get( + '/api/deployment/postgres/info', + ResponseType.Json + ); + + if (typeof res === 'object') { + return res; + } +}; diff --git a/frontend/src/lib/components/form/FormArea.svelte b/frontend/src/lib/components/form/FormArea.svelte new file mode 100644 index 0000000..342531e --- /dev/null +++ b/frontend/src/lib/components/form/FormArea.svelte @@ -0,0 +1,41 @@ + + + + + {#snippet children({ props })} + {label} +