v2 app deployment--templating mainly in manifest now.
This commit is contained in:
143
ADDING-APPS.md
143
ADDING-APPS.md
@@ -26,8 +26,9 @@ description: Immich is a self-hosted photo and video backup solution that allows
|
||||
version: 1.0.0
|
||||
icon: https://immich.app/assets/images/logo.png
|
||||
requires:
|
||||
- name: redis
|
||||
- name: postgres
|
||||
- name: pg
|
||||
alias: db # Use a different reference name in templates
|
||||
- name: redis # 'alias' and 'installedAs' default to 'name' value
|
||||
defaultConfig:
|
||||
serverImage: ghcr.io/immich-app/immich-server:release
|
||||
mlImage: ghcr.io/immich-app/immich-machine-learning:release
|
||||
@@ -36,13 +37,21 @@ defaultConfig:
|
||||
mlPort: 3003
|
||||
storage: 250Gi
|
||||
cacheStorage: 10Gi
|
||||
redisHostname: redis.redis.svc.cluster.local
|
||||
dbHostname: postgres.postgres.svc.cluster.local
|
||||
dbUsername: immich
|
||||
redisHostname: "{{ .apps.redis.host }}" # Can reference 'requires' app configurations
|
||||
dbHostname: "{{ .apps.pg.host }}"
|
||||
db: # Configuration can be nested
|
||||
name: immich
|
||||
user: immich
|
||||
host: "{{ .apps.pg.host }}"
|
||||
port: "{{ .apps.pg.port }}"
|
||||
domain: immich.{{ .cloud.domain }}
|
||||
defaultSecrets:
|
||||
- apps.immich.dbPassword
|
||||
- apps.postgres.password
|
||||
- key: password # Random value will be generated if empty
|
||||
- key: dbUrl
|
||||
default: "postgresql://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}?pool=30" # Can reference secrets and config as long as they have been defined before this line. Reference config with {{ .app.? }} and secrets with {{ .secrets.? }}
|
||||
requiredSecrets:
|
||||
- db.password # References postgres app via 'db' alias
|
||||
- redis.auth # References redis app via 'redis' name (no alias)
|
||||
```
|
||||
|
||||
#### Manifest Fields
|
||||
@@ -53,11 +62,31 @@ defaultSecrets:
|
||||
| `description` | Yes | Brief app description shown in listings |
|
||||
| `version` | Yes | App version (follow upstream versioning) |
|
||||
| `icon` | No | URL to app icon for UI display |
|
||||
| `requires` | No | List of dependency apps (e.g., `postgres`, `redis`) |
|
||||
| `requires` | No | List of dependency apps with optional aliases |
|
||||
| `defaultConfig` | Yes | Default configuration values merged into operator's `config.yaml` |
|
||||
| `defaultSecrets` | No | List of secrets in dotted-path format (e.g., `apps.appname.dbPassword`) |
|
||||
| `defaultSecrets` | No | This app's secrets (no 'default' = auto-generated) |
|
||||
| `requiredSecrets` | No | List of secrets from dependency apps (format: `<app-ref>.<key>`) |
|
||||
|
||||
**Important:** All configuration keys referenced in templates (via `{{ .apps.appname.key }}`) must be defined in `defaultConfig` or be standard Wild Cloud variables.
|
||||
**Dependency Configuration:**
|
||||
- Each dependency in `requires` can have:
|
||||
- `name`: The actual app name to depend on
|
||||
- `alias`: Optional reference name for templates (defaults to `name`)
|
||||
|
||||
**Manifest Template Variable Sources:**
|
||||
1. Standard Wild Cloud variables: `{{ .cloud.* }}`, `{{ .cluster.* }}`, `{{ .operator.* }}`
|
||||
2. App-specific variables: `{{ .app.* }}` - resolved from current app's config
|
||||
3. Dependency variables: `{{ .apps.<ref>.* }}` - resolved using app reference mapping
|
||||
4. App-specific secrets (in 'defaultSecrets' ONLY): `{{ secrets.* }}`
|
||||
|
||||
**Manifest App Reference Resolution:**
|
||||
When you use `{{ .apps.<ref>.* }}` in templates:
|
||||
1. System checks if `<ref>` matches any dependency's `alias` field
|
||||
2. If no alias match, checks if `<ref>` matches any dependency's `name` field
|
||||
3. Uses the `installedAs` value (automatically added when the app is added) to find actual app configuration in `config.yaml`
|
||||
|
||||
All manifest template variables must be defined in one of these locations.
|
||||
|
||||
**Important:** In the rest of the app templates, ALL configuration keys referenced in templates (via `{{ .key }}`) must be defined in `defaultConfig`. Only the app config is available to app templates.
|
||||
|
||||
### Kustomization (`kustomization.yaml`)
|
||||
|
||||
@@ -111,21 +140,7 @@ This means individual resources can use simple, component-specific selectors lik
|
||||
|
||||
### Gomplate Templating
|
||||
|
||||
Resource files in this repository are **templates** that get compiled when users add apps via the web app, CLI, or API. Use gomplate syntax to reference configuration:
|
||||
|
||||
```yaml
|
||||
# Common template variables
|
||||
domain: {{ .cloud.domain }} # Operator's domain
|
||||
email: {{ .operator.email }} # Operator's email
|
||||
image: {{ .apps.myapp.serverImage }} # App-specific config
|
||||
dbHost: {{ .apps.myapp.dbHostname }} # App-specific config
|
||||
```
|
||||
|
||||
**Template variable sources:**
|
||||
1. Standard Wild Cloud variables (`{{ .cloud.* }}`, `{{ .operator.* }}`)
|
||||
2. App-specific variables defined in your manifest's `defaultConfig`
|
||||
|
||||
All template variables must be defined in one of these locations. The compiled files are placed in the instance's directory as standard Kubernetes manifests.
|
||||
Resource files in this repository are **templates** that get compiled when users add apps via the web app, CLI, or API. Only variables defined in the manifest file's 'defaultConfig' section are available to the resource templates. Use gomplate syntax to reference configuration:
|
||||
|
||||
### External DNS
|
||||
|
||||
@@ -133,12 +148,47 @@ Ingress resources should include external-dns annotations for automatic DNS mana
|
||||
|
||||
```yaml
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
|
||||
external-dns.alpha.kubernetes.io/target: {{ .domain }}
|
||||
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
|
||||
```
|
||||
|
||||
Note: 'domain' must be defined in the app manifest's 'defaultConfig' section.
|
||||
|
||||
This creates a CNAME from the app subdomain to the cluster domain (e.g., `myapp.cloud.example.com` → `cloud.example.com`).
|
||||
|
||||
## App Dependencies and Reference Mapping
|
||||
|
||||
### How Dependency References Work
|
||||
|
||||
When an app depends on other apps, the reference system allows flexibility in naming while maintaining clear relationships:
|
||||
|
||||
1. **Define dependencies** in your manifest with optional aliases:
|
||||
```yaml
|
||||
requires:
|
||||
- name: postgres # Actual app to depend on
|
||||
alias: db # Optional: how to reference it in templates
|
||||
- name: redis # No alias means use 'redis' as reference
|
||||
```
|
||||
|
||||
2. **At installation time**, the system:
|
||||
- Prompts user to map dependencies to actual installed apps
|
||||
- Sets `installedAs` field in the local app manifest to track the mapping
|
||||
- Example: User might have `postgres-primary` installed, mapped to the `db` dependency
|
||||
|
||||
### Example: Multiple Database Instances
|
||||
|
||||
If a user has multiple PostgreSQL instances:
|
||||
```yaml
|
||||
# User's config.yaml
|
||||
apps:
|
||||
postgres-primary:
|
||||
hostname: primary.postgres.svc.cluster.local
|
||||
postgres-analytics:
|
||||
hostname: analytics.postgres.svc.cluster.local
|
||||
```
|
||||
|
||||
When adding an app that requires postgres, they can choose which instance to use, and the system tracks this in the manifest's `installedAs` field.
|
||||
|
||||
## Database Patterns
|
||||
|
||||
### Database Initialization Jobs
|
||||
@@ -211,13 +261,16 @@ spec:
|
||||
|
||||
### Secrets Management
|
||||
|
||||
Secrets use a **full dotted-path naming convention** to prevent naming conflicts:
|
||||
Secrets are managed through two mechanisms: default secrets for the app itself and required secrets from dependencies.
|
||||
|
||||
**In manifest:**
|
||||
```yaml
|
||||
defaultSecrets:
|
||||
- apps.myapp.dbPassword
|
||||
- apps.postgres.password
|
||||
key: dbPassword # This app's database password
|
||||
key: apiKey # This app's API key
|
||||
requiredSecrets:
|
||||
- db.password # Password from postgres dependency (aliased as 'db')
|
||||
- redis.auth # Auth from redis dependency
|
||||
```
|
||||
|
||||
**In resources:**
|
||||
@@ -227,14 +280,26 @@ env:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: myapp-secrets
|
||||
key: apps.myapp.dbPassword # Full dotted path, not just "dbPassword"
|
||||
key: dbPassword # Points to the default secret
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: myapp-secrets
|
||||
key: db.password # Points to the required secret
|
||||
```
|
||||
|
||||
**Secret workflow:**
|
||||
1. List secrets in manifest's `defaultSecrets`
|
||||
2. When adding an app, the system generates random values in the instance's `secrets.yaml`
|
||||
3. When deploying, the system creates a Kubernetes Secret named `<app-name>-secrets`
|
||||
4. Resources reference secrets using full dotted paths
|
||||
1. Define app's own secrets in `defaultSecrets` (key, default mappings)
|
||||
2. Reference dependency secrets in `requiredSecrets` (list)
|
||||
3. When adding an app, the system:
|
||||
- Generates random values for empty `defaultSecrets`
|
||||
- Copies referenced secrets from dependencies
|
||||
- Stores all in the instance's `secrets.yaml`
|
||||
4. When deploying, creates a Kubernetes Secret named `<app-name>-secrets` containing:
|
||||
- All `defaultSecrets` with key format: `<key>`
|
||||
- All `requiredSecrets` with key format: `<app-ref>.<key>`
|
||||
|
||||
**Key collision handling:** If the same key exists in both `defaultSecrets` and `requiredSecrets`, the `requiredSecrets` value takes precedence. Authors should ensure their local secrets don't collide with their required secrets.
|
||||
|
||||
**Important:** Never commit `secrets.yaml` to Git. Templates should only reference secrets, never contain actual secret values.
|
||||
|
||||
@@ -308,9 +373,11 @@ Before submitting a new or modified app, verify:
|
||||
- [ ] **Manifest**
|
||||
- [ ] `name` matches directory name
|
||||
- [ ] All required fields present (`name`, `description`, `version`, `defaultConfig`)
|
||||
- [ ] All template variables defined in `defaultConfig` or are standard Wild Cloud variables
|
||||
- [ ] Secrets use dotted-path format (e.g., `apps.appname.secretname`)
|
||||
- [ ] Dependencies listed in `requires` (if any)
|
||||
- [ ] All template variables defined in `defaultConfig`
|
||||
- [ ] `defaultSecrets` uses maps with 'key' and 'default' attributes
|
||||
- [ ] `requiredSecrets` references use `<app-ref>.<key>` format
|
||||
- [ ] Dependencies listed in `requires` with optional `alias` fields
|
||||
- [ ] Manifest template references match dependency aliases or names
|
||||
|
||||
- [ ] **Kustomization**
|
||||
- [ ] Includes standard Wild Cloud labels with `includeSelectors: true`
|
||||
@@ -318,8 +385,6 @@ Before submitting a new or modified app, verify:
|
||||
- [ ] All resource files listed under `resources:`
|
||||
|
||||
- [ ] **Resources**
|
||||
- [ ] All hardcoded values replaced with gomplate variables
|
||||
- [ ] Secrets reference full dotted paths
|
||||
- [ ] Security contexts on all pods (both pod-level and container-level)
|
||||
- [ ] Simple component labels, no Helm-style labels
|
||||
- [ ] Ingresses include external-dns annotations
|
||||
|
||||
Reference in New Issue
Block a user