Compare commits

...

3 Commits

Author SHA1 Message Date
Rostislav Dugin
12eea72392 FEATURE (helm): Use ClusterIP by default and add deployment to ghcr.io 2025-12-04 15:11:09 +03:00
Rostislav Dugin
75c88bac50 FIX (webhook): Escape webhook characters 2025-12-04 14:28:49 +03:00
Rostislav Dugin
ff1b6536bf FIX (connection): Add standard_conforming_strings param when building string to connect to PG 2025-12-03 18:42:49 +03:00
7 changed files with 190 additions and 100 deletions

View File

@@ -465,3 +465,37 @@ jobs:
body: ${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
publish-helm-chart:
runs-on: ubuntu-latest
needs: [determine-version, build-and-push]
if: ${{ needs.determine-version.outputs.should_release == 'true' }}
permissions:
contents: read
packages: write
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.14.0
- name: Log in to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Update Chart.yaml with release version
run: |
VERSION="${{ needs.determine-version.outputs.new_version }}"
sed -i "s/^version: .*/version: ${VERSION}/" deploy/helm/Chart.yaml
sed -i "s/^appVersion: .*/appVersion: \"v${VERSION}\"/" deploy/helm/Chart.yaml
cat deploy/helm/Chart.yaml
- name: Package Helm chart
run: helm package deploy/helm --destination .
- name: Push Helm chart to GHCR
run: |
VERSION="${{ needs.determine-version.outputs.new_version }}"
helm push postgresus-${VERSION}.tgz oci://ghcr.io/rostislavdugin/charts

View File

@@ -159,32 +159,43 @@ docker compose up -d
### Option 4: Kubernetes with Helm
For Kubernetes deployments, use the official Helm chart.
For Kubernetes deployments, install directly from the OCI registry.
**Step 1:** Clone the repository:
**With ClusterIP + port-forward (development/testing):**
```bash
git clone https://github.com/RostislavDugin/postgresus.git
cd postgresus
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus \
-n postgresus --create-namespace
```
**Step 2:** Install with Helm:
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace
kubectl port-forward svc/postgresus-service 4005:4005 -n postgresus
# Access at http://localhost:4005
```
**Step 3:** Get the external IP:
**With LoadBalancer (cloud environments):**
```bash
kubectl get svc -n postgresus
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus \
-n postgresus --create-namespace \
--set service.type=LoadBalancer
```
Access Postgresus at `http://<EXTERNAL-IP>` (port 80).
```bash
kubectl get svc postgresus-service -n postgresus
# Access at http://<EXTERNAL-IP>:4005
```
To customize the installation (e.g., storage size, NodePort instead of LoadBalancer), see the [Helm chart README](deploy/helm/README.md) for all configuration options.
**With Ingress (domain-based access):**
Config uses by default LoadBalancer, but has predefined values for Ingress and HTTPRoute as well.
```bash
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus \
-n postgresus --create-namespace \
--set ingress.enabled=true \
--set ingress.hosts[0].host=backup.example.com
```
For more options (NodePort, TLS, HTTPRoute for Gateway API), see the [Helm chart README](deploy/helm/README.md).
---

View File

@@ -594,7 +594,7 @@ func buildConnectionStringForDB(p *PostgresqlDatabase, dbName string, password s
}
return fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s default_query_exec_mode=simple_protocol",
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s default_query_exec_mode=simple_protocol standard_conforming_strings=on",
p.Host,
p.Port,
p.Username,

View File

@@ -206,8 +206,8 @@ func (t *WebhookNotifier) sendPOST(webhookURL, heading, message string, logger *
func (t *WebhookNotifier) buildRequestBody(heading, message string) []byte {
if t.BodyTemplate != nil && *t.BodyTemplate != "" {
result := *t.BodyTemplate
result = strings.ReplaceAll(result, "{{heading}}", heading)
result = strings.ReplaceAll(result, "{{message}}", message)
result = strings.ReplaceAll(result, "{{heading}}", escapeJSONString(heading))
result = strings.ReplaceAll(result, "{{message}}", escapeJSONString(message))
return []byte(result)
}
@@ -227,3 +227,17 @@ func (t *WebhookNotifier) applyHeaders(req *http.Request) {
}
}
}
func escapeJSONString(s string) string {
b, err := json.Marshal(s)
if err != nil || len(b) < 2 {
escaped := strings.ReplaceAll(s, `\`, `\\`)
escaped = strings.ReplaceAll(escaped, `"`, `\"`)
escaped = strings.ReplaceAll(escaped, "\n", `\n`)
escaped = strings.ReplaceAll(escaped, "\r", `\r`)
escaped = strings.ReplaceAll(escaped, "\t", `\t`)
return escaped
}
return string(b[1 : len(b)-1])
}

View File

@@ -2,11 +2,21 @@ apiVersion: v2
name: postgresus
description: A Helm chart for Postgresus - PostgreSQL backup and management system
type: application
version: 1.0.0
appVersion: "v1.45.3"
version: 0.0.0
appVersion: "latest"
keywords:
- postgresql
- backup
- database
- restore
home: https://github.com/RostislavDugin/postgresus
sources:
- https://github.com/RostislavDugin/postgresus
- https://github.com/RostislavDugin/postgresus/tree/main/deploy/helm
maintainers:
- name: Rostislav Dugin
url: https://github.com/RostislavDugin
icon: https://raw.githubusercontent.com/RostislavDugin/postgresus/main/frontend/public/logo.svg

View File

@@ -2,17 +2,21 @@
## Installation
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace
```
After installation, get the external IP:
Install directly from the OCI registry (no need to clone the repository):
```bash
kubectl get svc -n postgresus
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus --create-namespace
```
Access Postgresus at `http://<EXTERNAL-IP>` (port 80).
## Accessing Postgresus
By default, the chart creates a ClusterIP service. Use port-forward to access:
```bash
kubectl port-forward svc/postgresus-service 4005:4005 -n postgresus
```
Then open `http://localhost:4005` in your browser.
## Configuration
@@ -27,14 +31,14 @@ Access Postgresus at `http://<EXTERNAL-IP>` (port 80).
| `image.pullPolicy` | Image pull policy | `Always` |
| `replicaCount` | Number of replicas | `1` |
### Resources
### Service
| Parameter | Description | Default Value |
| --------------------------- | -------------- | ------------- |
| `resources.requests.memory` | Memory request | `1Gi` |
| `resources.requests.cpu` | CPU request | `500m` |
| `resources.limits.memory` | Memory limit | `1Gi` |
| `resources.limits.cpu` | CPU limit | `500m` |
| Parameter | Description | Default Value |
| -------------------------- | ----------------------- | ------------- |
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `4005` |
| `service.targetPort` | Container port | `4005` |
| `service.headless.enabled` | Enable headless service | `true` |
### Storage
@@ -46,93 +50,76 @@ Access Postgresus at `http://<EXTERNAL-IP>` (port 80).
| `persistence.size` | Storage size | `10Gi` |
| `persistence.mountPath` | Mount path | `/postgresus-data` |
### Service
### Resources
| Parameter | Description | Default Value |
| -------------------------- | ----------------------- | -------------- |
| `service.type` | Service type | `LoadBalancer` |
| `service.port` | External port | `80` |
| `service.targetPort` | Container port | `4005` |
| `service.headless.enabled` | Enable headless service | `true` |
| Parameter | Description | Default Value |
| --------------------------- | -------------- | ------------- |
| `resources.requests.memory` | Memory request | `1Gi` |
| `resources.requests.cpu` | CPU request | `500m` |
| `resources.limits.memory` | Memory limit | `1Gi` |
| `resources.limits.cpu` | CPU limit | `500m` |
### Traffic Exposure (3 Options)
## External Access Options
The chart supports 3 ways to expose Postgresus:
### Option 1: Port Forward (Default)
| Method | Use Case | Default |
| ------ | -------- | ------- |
| **LoadBalancer/NodePort** | Simple cloud clusters | Enabled |
| **Ingress** | Traditional nginx/traefik ingress controllers | Disabled |
| **HTTPRoute (Gateway API)** | Modern gateways (Istio, Envoy, Cilium) | Disabled |
#### Ingress
| Parameter | Description | Default Value |
| ----------------------- | ----------------- | ------------------------ |
| `ingress.enabled` | Enable Ingress | `false` |
| `ingress.className` | Ingress class | `nginx` |
| `ingress.hosts[0].host` | Hostname | `postgresus.example.com` |
| `ingress.tls` | TLS configuration | `[]` |
#### HTTPRoute (Gateway API)
| Parameter | Description | Default Value |
| --------------------- | -------------------------- | ---------------------------------- |
| `route.enabled` | Enable HTTPRoute | `false` |
| `route.apiVersion` | Gateway API version | `gateway.networking.k8s.io/v1` |
| `route.hostnames` | Hostnames for the route | `["postgresus.example.com"]` |
| `route.parentRefs` | Gateway references | `[]` |
| `route.annotations` | Route annotations | `{}` |
### Health Checks
| Parameter | Description | Default Value |
| ------------------------ | ---------------------- | ------------- |
| `livenessProbe.enabled` | Enable liveness probe | `true` |
| `readinessProbe.enabled` | Enable readiness probe | `true` |
## Examples
### Basic Installation (LoadBalancer on port 80)
Default installation exposes Postgresus via LoadBalancer on port 80:
Best for development or quick access:
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace
kubectl port-forward svc/postgresus-service 4005:4005 -n postgresus
```
Access via `http://<EXTERNAL-IP>`
Access at `http://localhost:4005`
### Using NodePort
### Option 2: NodePort
If your cluster doesn't support LoadBalancer:
For direct access via node IP:
```yaml
# nodeport-values.yaml
service:
type: NodePort
port: 80
port: 4005
targetPort: 4005
nodePort: 30080
```
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace -f nodeport-values.yaml
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus --create-namespace -f nodeport-values.yaml
```
Access via `http://<NODE-IP>:30080`
Access at `http://<NODE-IP>:30080`
### Enable Ingress with HTTPS
### Option 3: LoadBalancer
For cloud environments with load balancer support:
```yaml
# loadbalancer-values.yaml
service:
type: LoadBalancer
port: 80
targetPort: 4005
```
```bash
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus --create-namespace -f loadbalancer-values.yaml
```
Get the external IP:
```bash
kubectl get svc -n postgresus
```
Access at `http://<EXTERNAL-IP>`
### Option 4: Ingress
For domain-based access with TLS:
```yaml
# ingress-values.yaml
service:
type: ClusterIP
port: 4005
targetPort: 4005
ingress:
enabled: true
className: nginx
@@ -151,18 +138,15 @@ ingress:
```
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace -f ingress-values.yaml
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus --create-namespace -f ingress-values.yaml
```
### HTTPRoute (Gateway API)
### Option 5: HTTPRoute (Gateway API)
For clusters using Istio, Envoy Gateway, Cilium, or other Gateway API implementations:
```yaml
# httproute-values.yaml
service:
type: ClusterIP
route:
enabled: true
hostnames:
@@ -173,10 +157,35 @@ route:
```
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace -f httproute-values.yaml
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus --create-namespace -f httproute-values.yaml
```
### Custom Storage Size
## Ingress Configuration
| Parameter | Description | Default Value |
| ----------------------- | ----------------- | ------------------------ |
| `ingress.enabled` | Enable Ingress | `false` |
| `ingress.className` | Ingress class | `nginx` |
| `ingress.hosts[0].host` | Hostname | `postgresus.example.com` |
| `ingress.tls` | TLS configuration | `[]` |
## HTTPRoute Configuration
| Parameter | Description | Default Value |
| ------------------ | ----------------------- | ------------------------------ |
| `route.enabled` | Enable HTTPRoute | `false` |
| `route.apiVersion` | Gateway API version | `gateway.networking.k8s.io/v1` |
| `route.hostnames` | Hostnames for the route | `["postgresus.example.com"]` |
| `route.parentRefs` | Gateway references | `[]` |
## Health Checks
| Parameter | Description | Default Value |
| ------------------------ | ---------------------- | ------------- |
| `livenessProbe.enabled` | Enable liveness probe | `true` |
| `readinessProbe.enabled` | Enable readiness probe | `true` |
## Custom Storage Size
```yaml
# storage-values.yaml
@@ -186,5 +195,17 @@ persistence:
```
```bash
helm install postgresus ./deploy/helm -n postgresus --create-namespace -f storage-values.yaml
helm install postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus --create-namespace -f storage-values.yaml
```
## Upgrade
```bash
helm upgrade postgresus oci://ghcr.io/rostislavdugin/charts/postgresus -n postgresus
```
## Uninstall
```bash
helm uninstall postgresus -n postgresus
```

View File

@@ -16,8 +16,8 @@ replicaCount: 1
# Service configuration
service:
type: LoadBalancer
port: 80 # External port (HTTP default)
type: ClusterIP
port: 4005 # Service port
targetPort: 4005 # Internal container port
# Headless service for StatefulSet
headless: