mirror of
https://github.com/morten-olsen/homelab-apps.git
synced 2026-02-08 01:36:28 +01:00
migrate miniflux
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
name: miniflux
|
name: miniflux
|
||||||
|
dependencies:
|
||||||
|
- name: common
|
||||||
|
version: 1.0.0
|
||||||
|
repository: file://../../common
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
apiVersion: homelab.mortenolsen.pro/v1
|
|
||||||
kind: OidcClient
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}"
|
|
||||||
spec:
|
|
||||||
environment: "{{ .Values.globals.environment }}"
|
|
||||||
redirectUris:
|
|
||||||
- path: oauth2/oidc/callback
|
|
||||||
subdomain: "{{ .Values.subdomain }}"
|
|
||||||
matchingMode: strict
|
|
||||||
@@ -1,6 +1 @@
|
|||||||
apiVersion: homelab.mortenolsen.pro/v1
|
{{ include "common.database" . }}
|
||||||
kind: PostgresDatabase
|
|
||||||
metadata:
|
|
||||||
name: '{{ .Release.Name }}'
|
|
||||||
spec:
|
|
||||||
environment: '{{ .Values.globals.environment }}'
|
|
||||||
|
|||||||
@@ -1,75 +1 @@
|
|||||||
apiVersion: apps/v1
|
{{ include "common.deployment" . }}
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}"
|
|
||||||
spec:
|
|
||||||
strategy:
|
|
||||||
type: RollingUpdate
|
|
||||||
replicas: 1
|
|
||||||
revisionHistoryLimit: 0
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: "{{ .Release.Name }}"
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: "{{ .Release.Name }}"
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: "{{ .Release.Name }}"
|
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
||||||
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: http
|
|
||||||
readinessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: http
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /data
|
|
||||||
name: data
|
|
||||||
env:
|
|
||||||
- name: TZ
|
|
||||||
value: "{{ .Values.globals.timezone }}"
|
|
||||||
- name: BASE_URL
|
|
||||||
value: https://{{ .Values.subdomain }}.{{ .Values.globals.domain }}
|
|
||||||
- name: RUN_MIGRATIONS
|
|
||||||
value: "1"
|
|
||||||
- name: DISABLE_LOCAL_AUTH
|
|
||||||
value: "1"
|
|
||||||
- name: OAUTH2_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-client"
|
|
||||||
key: clientId
|
|
||||||
- name: OAUTH2_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-client"
|
|
||||||
key: clientSecret
|
|
||||||
- name: OAUTH2_OIDC_DISCOVERY_ENDPOINT
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-client"
|
|
||||||
key: configurationIssuer
|
|
||||||
- name: OAUTH2_OIDC_PROVIDER_NAME
|
|
||||||
value: Authentik
|
|
||||||
- name: OAUTH2_PROVIDER
|
|
||||||
value: oidc
|
|
||||||
- name: OAUTH2_REDIRECT_URL
|
|
||||||
value: https://{{ .Values.subdomain }}.{{ .Values.globals.domain }}/oauth2/oidc/callback
|
|
||||||
- name: OAUTH2_USER_CREATION
|
|
||||||
value: "1"
|
|
||||||
- name: DATABASE_URL
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-pg-connection"
|
|
||||||
key: url
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: "{{ .Release.Name }}-data"
|
|
||||||
|
|||||||
1
apps/charts/miniflux/templates/oidc.yaml
Normal file
1
apps/charts/miniflux/templates/oidc.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{ include "common.oidc" . }}
|
||||||
@@ -1,11 +1 @@
|
|||||||
kind: PersistentVolumeClaim
|
{{ include "common.pvc" . }}
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: '{{ .Release.Name }}-data'
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- 'ReadWriteOnce'
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: '1Gi'
|
|
||||||
storageClassName: '{{ .Values.globals.environment }}'
|
|
||||||
|
|||||||
@@ -1,15 +1 @@
|
|||||||
apiVersion: v1
|
{{ include "common.service" . }}
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}"
|
|
||||||
labels:
|
|
||||||
app: "{{ .Release.Name }}"
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app: "{{ .Release.Name }}"
|
|
||||||
|
|||||||
@@ -1,39 +1 @@
|
|||||||
apiVersion: networking.istio.io/v1
|
{{ include "common.virtualService" . }}
|
||||||
kind: VirtualService
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}-public"
|
|
||||||
namespace: "{{ .Release.Namespace }}"
|
|
||||||
spec:
|
|
||||||
gateways:
|
|
||||||
- "{{ .Values.globals.istio.gateways.public }}"
|
|
||||||
- mesh
|
|
||||||
hosts:
|
|
||||||
- "{{ .Values.subdomain }}.{{ .Values.globals.domain }}"
|
|
||||||
- mesh
|
|
||||||
http:
|
|
||||||
- route:
|
|
||||||
- destination:
|
|
||||||
host: "{{ .Release.Name }}"
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: networking.istio.io/v1
|
|
||||||
kind: VirtualService
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}-private"
|
|
||||||
namespace: "{{ .Release.Namespace }}"
|
|
||||||
spec:
|
|
||||||
gateways:
|
|
||||||
- "{{ .Values.globals.istio.gateways.private }}"
|
|
||||||
- mesh
|
|
||||||
hosts:
|
|
||||||
- "{{ .Values.subdomain }}.{{ .Values.globals.domain }}"
|
|
||||||
- mesh
|
|
||||||
http:
|
|
||||||
- route:
|
|
||||||
- destination:
|
|
||||||
host: "{{ .Release.Name }}"
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,85 @@ image:
|
|||||||
repository: ghcr.io/miniflux/miniflux
|
repository: ghcr.io/miniflux/miniflux
|
||||||
tag: latest@sha256:9a1f95a7a05b77040d19bb7be96194af4c222de02fb84165fb808f65700c064f
|
tag: latest@sha256:9a1f95a7a05b77040d19bb7be96194af4c222de02fb84165fb808f65700c064f
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
subdomain: miniflux
|
subdomain: miniflux
|
||||||
|
|
||||||
|
# Deployment configuration
|
||||||
|
deployment:
|
||||||
|
strategy: RollingUpdate
|
||||||
|
replicas: 1
|
||||||
|
revisionHistoryLimit: 0
|
||||||
|
|
||||||
|
# Container configuration
|
||||||
|
container:
|
||||||
|
port: 8080
|
||||||
|
healthProbe:
|
||||||
|
type: tcpSocket
|
||||||
|
port: http # Use named port
|
||||||
|
|
||||||
|
# Service configuration
|
||||||
|
service:
|
||||||
|
port: 80
|
||||||
|
type: ClusterIP
|
||||||
|
|
||||||
|
# Volume configuration
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
persistentVolumeClaim: data
|
||||||
|
|
||||||
|
# Persistent volume claims
|
||||||
|
persistentVolumeClaims:
|
||||||
|
- name: data
|
||||||
|
size: 1Gi
|
||||||
|
|
||||||
|
# VirtualService configuration
|
||||||
|
virtualService:
|
||||||
|
enabled: true
|
||||||
|
gateways:
|
||||||
|
public: true
|
||||||
|
private: true
|
||||||
|
|
||||||
|
# OIDC client
|
||||||
|
oidc:
|
||||||
|
enabled: true
|
||||||
|
redirectUris:
|
||||||
|
- "/oauth2/oidc/callback"
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
|
database:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
env:
|
||||||
|
TZ:
|
||||||
|
value: "{timezone}"
|
||||||
|
BASE_URL:
|
||||||
|
value: "https://{subdomain}.{domain}"
|
||||||
|
RUN_MIGRATIONS: "1"
|
||||||
|
DISABLE_LOCAL_AUTH: "1"
|
||||||
|
OAUTH2_CLIENT_ID:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials"
|
||||||
|
key: clientId
|
||||||
|
OAUTH2_CLIENT_SECRET:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials"
|
||||||
|
key: clientSecret
|
||||||
|
OAUTH2_OIDC_DISCOVERY_ENDPOINT:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials"
|
||||||
|
key: issuer
|
||||||
|
OAUTH2_OIDC_PROVIDER_NAME: Authentik
|
||||||
|
OAUTH2_PROVIDER: oidc
|
||||||
|
OAUTH2_REDIRECT_URL:
|
||||||
|
value: "https://{subdomain}.{domain}/oauth2/oidc/callback"
|
||||||
|
OAUTH2_USER_CREATION: "1"
|
||||||
|
DATABASE_URL:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-connection"
|
||||||
|
key: url
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ This guide explains how to migrate existing Helm charts to use the common librar
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Migrating a chart to use the common library involves:
|
Migrating a chart to use the common library involves:
|
||||||
|
|
||||||
1. Adding the common library as a dependency
|
1. Adding the common library as a dependency
|
||||||
2. Restructuring `values.yaml` to match the standardized format
|
2. Restructuring `values.yaml` to match the standardized format
|
||||||
3. Replacing template files with simple includes
|
3. Replacing template files with simple includes
|
||||||
@@ -37,7 +38,8 @@ dependencies:
|
|||||||
|
|
||||||
Convert your existing `values.yaml` to the standardized format:
|
Convert your existing `values.yaml` to the standardized format:
|
||||||
|
|
||||||
#### Before (Old Format):
|
#### Before (Old Format)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image:
|
image:
|
||||||
repository: docker.io/org/app
|
repository: docker.io/org/app
|
||||||
@@ -45,7 +47,8 @@ image:
|
|||||||
subdomain: myapp
|
subdomain: myapp
|
||||||
```
|
```
|
||||||
|
|
||||||
#### After (Standardized Format):
|
#### After (Standardized Format)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image:
|
image:
|
||||||
repository: docker.io/org/app
|
repository: docker.io/org/app
|
||||||
@@ -95,6 +98,7 @@ oidc:
|
|||||||
enabled: true
|
enabled: true
|
||||||
redirectUris:
|
redirectUris:
|
||||||
- "/api/auth/callback/authentik"
|
- "/api/auth/callback/authentik"
|
||||||
|
subjectMode: user_username # Optional, defaults to "user_username"
|
||||||
|
|
||||||
# Database configuration (if applicable)
|
# Database configuration (if applicable)
|
||||||
database:
|
database:
|
||||||
@@ -117,71 +121,89 @@ env:
|
|||||||
Replace your template files with simple includes:
|
Replace your template files with simple includes:
|
||||||
|
|
||||||
#### deployment.yaml
|
#### deployment.yaml
|
||||||
|
|
||||||
**Before:** ~50-100 lines of template code
|
**Before:** ~50-100 lines of template code
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.deployment" . }}
|
{{ include "common.deployment" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### service.yaml
|
#### service.yaml
|
||||||
|
|
||||||
**Before:** ~15-20 lines
|
**Before:** ~15-20 lines
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.service" . }}
|
{{ include "common.service" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### pvc.yaml
|
#### pvc.yaml
|
||||||
|
|
||||||
**Before:** ~20-30 lines per PVC
|
**Before:** ~20-30 lines per PVC
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.pvc" . }}
|
{{ include "common.pvc" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### virtual-service.yaml
|
#### virtual-service.yaml
|
||||||
|
|
||||||
**Before:** ~40-50 lines
|
**Before:** ~40-50 lines
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.virtualService" . }}
|
{{ include "common.virtualService" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### dns.yaml (if applicable)
|
#### dns.yaml (if applicable)
|
||||||
|
|
||||||
**Before:** ~20 lines
|
**Before:** ~20 lines
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.dns" . }}
|
{{ include "common.dns" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### oidc.yaml (if applicable)
|
#### oidc.yaml (if applicable)
|
||||||
|
|
||||||
**Before:** ~20 lines
|
**Before:** ~20 lines
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.oidc" . }}
|
{{ include "common.oidc" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### database.yaml (if applicable)
|
#### database.yaml (if applicable)
|
||||||
|
|
||||||
**Before:** ~10 lines
|
**Before:** ~10 lines
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.database" . }}
|
{{ include "common.database" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### secret.yaml (if using External Secrets)
|
#### secret.yaml (if using External Secrets)
|
||||||
|
|
||||||
**Before:** ~10 lines (GenerateSecret)
|
**Before:** ~10 lines (GenerateSecret)
|
||||||
**After (recommended - split files for correct ordering):**
|
**After (recommended - split files for correct ordering):**
|
||||||
|
|
||||||
Create two files:
|
Create two files:
|
||||||
|
|
||||||
`templates/secret-password-generators.yaml`:
|
`templates/secret-password-generators.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.externalSecrets.passwordGenerators" . }}
|
{{ include "common.externalSecrets.passwordGenerators" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
`templates/secret-external-secrets.yaml`:
|
`templates/secret-external-secrets.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.externalSecrets.externalSecrets" . }}
|
{{ include "common.externalSecrets.externalSecrets" . }}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Alternative (single file):**
|
**Alternative (single file):**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{{ include "common.externalSecrets" . }}
|
{{ include "common.externalSecrets" . }}
|
||||||
```
|
```
|
||||||
@@ -216,6 +238,7 @@ helm template your-app apps/charts/your-app \
|
|||||||
```
|
```
|
||||||
|
|
||||||
Verify:
|
Verify:
|
||||||
|
|
||||||
- All resources render correctly
|
- All resources render correctly
|
||||||
- Environment variables use placeholders correctly
|
- Environment variables use placeholders correctly
|
||||||
- Ports and volumes are configured properly
|
- Ports and volumes are configured properly
|
||||||
@@ -372,11 +395,13 @@ See [TEMPLATING.md](./TEMPLATING.md) for complete placeholder documentation.
|
|||||||
### Example 1: Simple Application (audiobookshelf)
|
### Example 1: Simple Application (audiobookshelf)
|
||||||
|
|
||||||
**Before:**
|
**Before:**
|
||||||
|
|
||||||
- 6 template files with ~169 total lines
|
- 6 template files with ~169 total lines
|
||||||
- Custom health probe configuration
|
- Custom health probe configuration
|
||||||
- Multiple PVCs
|
- Multiple PVCs
|
||||||
|
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
- 6 template files with 6 total lines (one include each)
|
- 6 template files with 6 total lines (one include each)
|
||||||
- Standardized health probe using `/ping` endpoint
|
- Standardized health probe using `/ping` endpoint
|
||||||
- Same functionality, 96% less code
|
- Same functionality, 96% less code
|
||||||
@@ -384,11 +409,13 @@ See [TEMPLATING.md](./TEMPLATING.md) for complete placeholder documentation.
|
|||||||
### Example 2: Multi-Port Application (forgejo)
|
### Example 2: Multi-Port Application (forgejo)
|
||||||
|
|
||||||
**Before:**
|
**Before:**
|
||||||
|
|
||||||
- Multiple services (HTTP + SSH)
|
- Multiple services (HTTP + SSH)
|
||||||
- Complex port configuration
|
- Complex port configuration
|
||||||
- Multiple container ports
|
- Multiple container ports
|
||||||
|
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
- Uses `container.ports` array
|
- Uses `container.ports` array
|
||||||
- Uses `service.ports` array
|
- Uses `service.ports` array
|
||||||
- Each service can have different type (ClusterIP vs LoadBalancer)
|
- Each service can have different type (ClusterIP vs LoadBalancer)
|
||||||
@@ -396,11 +423,13 @@ See [TEMPLATING.md](./TEMPLATING.md) for complete placeholder documentation.
|
|||||||
### Example 3: Application with Database (blinko)
|
### Example 3: Application with Database (blinko)
|
||||||
|
|
||||||
**Before:**
|
**Before:**
|
||||||
|
|
||||||
- Environment variables with template syntax
|
- Environment variables with template syntax
|
||||||
- Secret references
|
- Secret references
|
||||||
- Database connection strings
|
- Database connection strings
|
||||||
|
|
||||||
**After:**
|
**After:**
|
||||||
|
|
||||||
- Environment variables use placeholders
|
- Environment variables use placeholders
|
||||||
- Secret references use `{release}` placeholder
|
- Secret references use `{release}` placeholder
|
||||||
- Cleaner, more maintainable values.yaml
|
- Cleaner, more maintainable values.yaml
|
||||||
@@ -430,6 +459,7 @@ Create `templates/database.yaml`:
|
|||||||
### Generated Secret
|
### Generated Secret
|
||||||
|
|
||||||
The PostgresDatabase resource creates a secret named `{release}-connection` containing:
|
The PostgresDatabase resource creates a secret named `{release}-connection` containing:
|
||||||
|
|
||||||
- `url` - Complete PostgreSQL connection URL
|
- `url` - Complete PostgreSQL connection URL
|
||||||
- `host` - Database hostname
|
- `host` - Database hostname
|
||||||
- `port` - Database port
|
- `port` - Database port
|
||||||
@@ -485,10 +515,12 @@ When migrating databases from the old PostgreSQL server (`prod-postgres-cluster-
|
|||||||
#### Database Naming Convention
|
#### Database Naming Convention
|
||||||
|
|
||||||
Database names follow the pattern `{namespace}_{name}` where:
|
Database names follow the pattern `{namespace}_{name}` where:
|
||||||
|
|
||||||
- `{namespace}` is the Kubernetes namespace (default: `prod`)
|
- `{namespace}` is the Kubernetes namespace (default: `prod`)
|
||||||
- `{name}` is the application name (release name)
|
- `{name}` is the application name (release name)
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
- `prod_blinko` - blinko app in prod namespace
|
- `prod_blinko` - blinko app in prod namespace
|
||||||
- `prod_gitea` - gitea app in prod namespace
|
- `prod_gitea` - gitea app in prod namespace
|
||||||
- `shared_authentik-db` - authentik app in shared namespace
|
- `shared_authentik-db` - authentik app in shared namespace
|
||||||
@@ -496,28 +528,35 @@ Database names follow the pattern `{namespace}_{name}` where:
|
|||||||
#### Using the Migration Script
|
#### Using the Migration Script
|
||||||
|
|
||||||
The migration script is located at `scripts/migrate_database.py` and handles:
|
The migration script is located at `scripts/migrate_database.py` and handles:
|
||||||
|
|
||||||
- Dumping the database from the old server
|
- Dumping the database from the old server
|
||||||
- Restoring to the new server
|
- Restoring to the new server
|
||||||
- Fixing permissions and ownership automatically
|
- Fixing permissions and ownership automatically
|
||||||
|
|
||||||
|
The script can not be used until the new database is deployed, so it shouldn't be used as part of the migration
|
||||||
|
|
||||||
**Basic Usage:**
|
**Basic Usage:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/migrate_database.py <source_db_name> <dest_db_name>
|
./scripts/migrate_database.py <source_db_name> <dest_db_name>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Migrate prod_blinko database (same name on both servers)
|
# Migrate prod_blinko database (same name on both servers)
|
||||||
./scripts/migrate_database.py prod_blinko prod_blinko
|
./scripts/migrate_database.py prod_blinko prod_blinko
|
||||||
```
|
```
|
||||||
|
|
||||||
**With Different Database Names:**
|
**With Different Database Names:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Migrate from old_name to new_name
|
# Migrate from old_name to new_name
|
||||||
./scripts/migrate_database.py old_name new_name
|
./scripts/migrate_database.py old_name new_name
|
||||||
```
|
```
|
||||||
|
|
||||||
**With Custom PostgreSQL Users:**
|
**With Custom PostgreSQL Users:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# If the PostgreSQL users differ from defaults
|
# If the PostgreSQL users differ from defaults
|
||||||
./scripts/migrate_database.py prod_blinko prod_blinko \
|
./scripts/migrate_database.py prod_blinko prod_blinko \
|
||||||
@@ -526,6 +565,7 @@ The migration script is located at `scripts/migrate_database.py` and handles:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Overwriting Existing Data:**
|
**Overwriting Existing Data:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Use --clean flag to drop existing objects before restoring
|
# Use --clean flag to drop existing objects before restoring
|
||||||
# WARNING: This will DELETE all existing data in the destination database!
|
# WARNING: This will DELETE all existing data in the destination database!
|
||||||
@@ -535,6 +575,7 @@ The migration script is located at `scripts/migrate_database.py` and handles:
|
|||||||
#### Behavior with Existing Databases
|
#### Behavior with Existing Databases
|
||||||
|
|
||||||
**Without `--clean` flag:**
|
**Without `--clean` flag:**
|
||||||
|
|
||||||
- The script will attempt to restore objects to the destination database
|
- The script will attempt to restore objects to the destination database
|
||||||
- If tables/objects already exist, `pg_restore` may:
|
- If tables/objects already exist, `pg_restore` may:
|
||||||
- Fail with errors (e.g., "relation already exists")
|
- Fail with errors (e.g., "relation already exists")
|
||||||
@@ -543,12 +584,14 @@ The migration script is located at `scripts/migrate_database.py` and handles:
|
|||||||
- **This will NOT automatically overwrite existing data**
|
- **This will NOT automatically overwrite existing data**
|
||||||
|
|
||||||
**With `--clean` flag:**
|
**With `--clean` flag:**
|
||||||
|
|
||||||
- Drops all existing objects (tables, sequences, functions, etc.) before restoring
|
- Drops all existing objects (tables, sequences, functions, etc.) before restoring
|
||||||
- **WARNING: This will DELETE all existing data in the destination database**
|
- **WARNING: This will DELETE all existing data in the destination database**
|
||||||
- Use this when you want to completely replace the destination database with source data
|
- Use this when you want to completely replace the destination database with source data
|
||||||
- Recommended for initial migrations or when you're sure you want to overwrite
|
- Recommended for initial migrations or when you're sure you want to overwrite
|
||||||
|
|
||||||
**Best Practice:**
|
**Best Practice:**
|
||||||
|
|
||||||
- For initial migrations: Use `--clean` to ensure a clean restore
|
- For initial migrations: Use `--clean` to ensure a clean restore
|
||||||
- For updates/re-syncs: Use `--clean` only if you're certain you want to replace all data
|
- For updates/re-syncs: Use `--clean` only if you're certain you want to replace all data
|
||||||
- For incremental updates: Consider using application-specific sync mechanisms instead
|
- For incremental updates: Consider using application-specific sync mechanisms instead
|
||||||
@@ -574,6 +617,7 @@ The migration script is located at `scripts/migrate_database.py` and handles:
|
|||||||
#### Default Configuration
|
#### Default Configuration
|
||||||
|
|
||||||
The script uses these defaults:
|
The script uses these defaults:
|
||||||
|
|
||||||
- **Source server**: `prod-postgres-cluster-0` in `homelab` namespace
|
- **Source server**: `prod-postgres-cluster-0` in `homelab` namespace
|
||||||
- **Source user**: `homelab`
|
- **Source user**: `homelab`
|
||||||
- **Destination server**: `postgres-statefulset-0` in `shared` namespace
|
- **Destination server**: `postgres-statefulset-0` in `shared` namespace
|
||||||
@@ -582,16 +626,20 @@ The script uses these defaults:
|
|||||||
#### Troubleshooting
|
#### Troubleshooting
|
||||||
|
|
||||||
**Error: "role does not exist"**
|
**Error: "role does not exist"**
|
||||||
|
|
||||||
- Check the PostgreSQL user name with: `kubectl exec -n <namespace> <pod> -c <container> -- env | grep POSTGRES_USER`
|
- Check the PostgreSQL user name with: `kubectl exec -n <namespace> <pod> -c <container> -- env | grep POSTGRES_USER`
|
||||||
- Use `--source-user` or `--dest-user` flags to specify correct users
|
- Use `--source-user` or `--dest-user` flags to specify correct users
|
||||||
|
|
||||||
**Error: "database does not exist"**
|
**Error: "database does not exist"**
|
||||||
|
|
||||||
- Create the destination database manually before running the script
|
- Create the destination database manually before running the script
|
||||||
- Verify database names match the `{namespace}_{name}` convention
|
- Verify database names match the `{namespace}_{name}` convention
|
||||||
|
|
||||||
**Error: "permission denied for schema"**
|
**Error: "permission denied for schema"**
|
||||||
|
|
||||||
- The script should fix this automatically
|
- The script should fix this automatically
|
||||||
- If issues persist, manually grant permissions:
|
- If issues persist, manually grant permissions:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
GRANT USAGE ON SCHEMA <schema_name> TO <db_user>;
|
GRANT USAGE ON SCHEMA <schema_name> TO <db_user>;
|
||||||
GRANT CREATE ON SCHEMA <schema_name> TO <db_user>;
|
GRANT CREATE ON SCHEMA <schema_name> TO <db_user>;
|
||||||
@@ -606,9 +654,81 @@ Some charts may still have legacy resources that should be kept as-is:
|
|||||||
- **PostgresDatabase** (legacy `homelab.mortenolsen.pro/v1`) - Use `common.database` for new PostgresDatabase instead
|
- **PostgresDatabase** (legacy `homelab.mortenolsen.pro/v1`) - Use `common.database` for new PostgresDatabase instead
|
||||||
- **GenerateSecret** (legacy `homelab.mortenolsen.pro/v1`) - Use `common.externalSecrets` for External Secrets instead
|
- **GenerateSecret** (legacy `homelab.mortenolsen.pro/v1`) - Use `common.externalSecrets` for External Secrets instead
|
||||||
|
|
||||||
|
### Migrating from OidcClient to AuthentikClient
|
||||||
|
|
||||||
|
**Before (OidcClient):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# templates/client.yaml
|
||||||
|
apiVersion: homelab.mortenolsen.pro/v1
|
||||||
|
kind: OidcClient
|
||||||
|
metadata:
|
||||||
|
name: "{{ .Release.Name }}"
|
||||||
|
spec:
|
||||||
|
environment: "{{ .Values.globals.environment }}"
|
||||||
|
redirectUris:
|
||||||
|
- path: oauth2/oidc/callback
|
||||||
|
subdomain: "{{ .Values.subdomain }}"
|
||||||
|
matchingMode: strict
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (AuthentikClient):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# values.yaml
|
||||||
|
oidc:
|
||||||
|
enabled: true
|
||||||
|
redirectUris:
|
||||||
|
- "/oauth2/oidc/callback" # Path only, domain is automatically prepended
|
||||||
|
subjectMode: user_username # Optional, defaults to "user_username"
|
||||||
|
|
||||||
|
# templates/client.yaml (or oidc.yaml)
|
||||||
|
{{ include "common.oidc" . }}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Changes:**
|
||||||
|
|
||||||
|
1. **API Version**: Changed from `homelab.mortenolsen.pro/v1` to `authentik.homelab.mortenolsen.pro/v1alpha1`
|
||||||
|
2. **Resource Kind**: Changed from `OidcClient` to `AuthentikClient`
|
||||||
|
3. **Redirect URIs**: Now specified as paths only (e.g., `"/oauth2/oidc/callback"`). The full URL is automatically constructed as `https://{subdomain}.{domain}{path}`
|
||||||
|
4. **Subject Mode**: New `subjectMode` field defaults to `"user_username"` but can be customized
|
||||||
|
5. **Secret Name**: The generated secret name changed from `{release}-client` to `{release}-oidc-credentials`
|
||||||
|
6. **Secret Keys**: The secret key `configurationIssuer` changed to `issuer`
|
||||||
|
|
||||||
|
**Environment Variable Updates:**
|
||||||
|
|
||||||
|
When migrating, update your environment variables to reference the new secret:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
OAUTH2_CLIENT_ID:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials" # Changed from {release}-client
|
||||||
|
key: clientId
|
||||||
|
OAUTH2_CLIENT_SECRET:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials" # Changed from {release}-client
|
||||||
|
key: clientSecret
|
||||||
|
OAUTH2_OIDC_DISCOVERY_ENDPOINT:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials" # Changed from {release}-client
|
||||||
|
key: issuer # Changed from configurationIssuer
|
||||||
|
```
|
||||||
|
|
||||||
|
**Subject Mode Options:**
|
||||||
|
|
||||||
|
The `subjectMode` field controls how the subject identifier is generated:
|
||||||
|
- `user_username` (default) - Uses the username as the subject identifier
|
||||||
|
- `user_email` - Uses the email address as the subject identifier
|
||||||
|
- `user_id` - Uses the user ID as the subject identifier
|
||||||
|
|
||||||
### Migrating from GenerateSecret to External Secrets
|
### Migrating from GenerateSecret to External Secrets
|
||||||
|
|
||||||
**Before (GenerateSecret):**
|
**Before (GenerateSecret):**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# templates/secret.yaml
|
# templates/secret.yaml
|
||||||
apiVersion: homelab.mortenolsen.pro/v1
|
apiVersion: homelab.mortenolsen.pro/v1
|
||||||
@@ -623,6 +743,7 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**After (External Secrets):**
|
**After (External Secrets):**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# values.yaml
|
# values.yaml
|
||||||
externalSecrets:
|
externalSecrets:
|
||||||
@@ -641,6 +762,7 @@ externalSecrets:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Note:**
|
**Note:**
|
||||||
|
|
||||||
- External Secrets generates passwords directly (no encoding option)
|
- External Secrets generates passwords directly (no encoding option)
|
||||||
- The `secretKeys` field is **required** to set the key name in the secret
|
- The `secretKeys` field is **required** to set the key name in the secret
|
||||||
- Without `secretKeys`, the Password generator defaults to using `password` as the key name
|
- Without `secretKeys`, the Password generator defaults to using `password` as the key name
|
||||||
@@ -653,6 +775,7 @@ externalSecrets:
|
|||||||
**Error:** `found in Chart.yaml, but missing in charts/ directory: common`
|
**Error:** `found in Chart.yaml, but missing in charts/ directory: common`
|
||||||
|
|
||||||
**Solution:**
|
**Solution:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd apps/charts/your-app
|
cd apps/charts/your-app
|
||||||
rm -rf charts Chart.lock
|
rm -rf charts Chart.lock
|
||||||
@@ -666,6 +789,7 @@ helm dependency build
|
|||||||
**Error:** Template rendering fails with syntax errors
|
**Error:** Template rendering fails with syntax errors
|
||||||
|
|
||||||
**Solution:**
|
**Solution:**
|
||||||
|
|
||||||
- Ensure all placeholders use curly braces: `{release}`, not `{{release}}`
|
- Ensure all placeholders use curly braces: `{release}`, not `{{release}}`
|
||||||
- Check that values.yaml uses proper YAML structure
|
- Check that values.yaml uses proper YAML structure
|
||||||
- Verify globals are provided when testing
|
- Verify globals are provided when testing
|
||||||
@@ -675,6 +799,7 @@ helm dependency build
|
|||||||
**Problem:** Placeholders like `{subdomain}` appear literally in output
|
**Problem:** Placeholders like `{subdomain}` appear literally in output
|
||||||
|
|
||||||
**Solution:**
|
**Solution:**
|
||||||
|
|
||||||
- Ensure you're using the latest common library version
|
- Ensure you're using the latest common library version
|
||||||
- Rebuild dependencies: `helm dependency build`
|
- Rebuild dependencies: `helm dependency build`
|
||||||
- Check that placeholders are in `env:` section, not elsewhere
|
- Check that placeholders are in `env:` section, not elsewhere
|
||||||
@@ -684,6 +809,7 @@ helm dependency build
|
|||||||
**Problem:** Health probe uses wrong port or type
|
**Problem:** Health probe uses wrong port or type
|
||||||
|
|
||||||
**Solution:**
|
**Solution:**
|
||||||
|
|
||||||
- For named ports, use: `port: http` (the port name)
|
- For named ports, use: `port: http` (the port name)
|
||||||
- For numeric ports, use: `port: 80` (the port number)
|
- For numeric ports, use: `port: 80` (the port number)
|
||||||
- Ensure `container.healthProbe.type` is set correctly
|
- Ensure `container.healthProbe.type` is set correctly
|
||||||
@@ -693,6 +819,7 @@ helm dependency build
|
|||||||
**Problem:** Only one service is created when multiple are expected
|
**Problem:** Only one service is created when multiple are expected
|
||||||
|
|
||||||
**Solution:**
|
**Solution:**
|
||||||
|
|
||||||
- Use `service.ports` array (not `service.port`)
|
- Use `service.ports` array (not `service.port`)
|
||||||
- Each port entry creates a separate service
|
- Each port entry creates a separate service
|
||||||
- Use `serviceName` in port config for custom names
|
- Use `serviceName` in port config for custom names
|
||||||
|
|||||||
@@ -75,6 +75,39 @@ Helper functions for custom templates:
|
|||||||
|
|
||||||
The library expects a standardized values structure. See migrated charts (`audiobookshelf`, `forgejo`, `baikal`, `blinko`) for examples.
|
The library expects a standardized values structure. See migrated charts (`audiobookshelf`, `forgejo`, `baikal`, `blinko`) for examples.
|
||||||
|
|
||||||
|
### OIDC Configuration
|
||||||
|
|
||||||
|
To enable OIDC authentication, configure the `oidc` section in your `values.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
oidc:
|
||||||
|
enabled: true
|
||||||
|
redirectUris:
|
||||||
|
- "/api/auth/callback/authentik"
|
||||||
|
subjectMode: user_username # Optional, defaults to "user_username"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Subject Mode Options:**
|
||||||
|
- `user_username` (default) - Uses the username as the subject identifier
|
||||||
|
- `user_email` - Uses the email address as the subject identifier
|
||||||
|
- `user_id` - Uses the user ID as the subject identifier
|
||||||
|
|
||||||
|
The AuthentikClient resource creates a secret named `{release}-oidc-credentials` containing:
|
||||||
|
- `clientId` - OAuth client ID
|
||||||
|
- `clientSecret` - OAuth client secret
|
||||||
|
- `issuer` - OIDC provider issuer URL
|
||||||
|
|
||||||
|
Reference these in your environment variables using placeholders:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
OAUTH2_CLIENT_ID:
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{release}-oidc-credentials"
|
||||||
|
key: clientId
|
||||||
|
```
|
||||||
|
|
||||||
## Placeholders
|
## Placeholders
|
||||||
|
|
||||||
Use placeholders in `values.yaml` for dynamic values:
|
Use placeholders in `values.yaml` for dynamic values:
|
||||||
|
|||||||
@@ -521,6 +521,7 @@ spec:
|
|||||||
{{- range .Values.oidc.redirectUris }}
|
{{- range .Values.oidc.redirectUris }}
|
||||||
- {{ printf "https://%s%s" (include "common.domain" $) . | quote }}
|
- {{ printf "https://%s%s" (include "common.domain" $) . | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
subjectMode: {{ .Values.oidc.subjectMode | default "user_username" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user