mirror of
https://github.com/morten-olsen/homelab-apps.git
synced 2026-02-08 01:36:28 +01:00
secret templatte documentation
This commit is contained in:
389
AGENTS.md
389
AGENTS.md
@@ -58,6 +58,7 @@ mkdir -p apps/charts/my-app/templates
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Chart.yaml
|
#### Chart.yaml
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
@@ -65,6 +66,7 @@ name: my-app
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### values.yaml
|
#### values.yaml
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image:
|
image:
|
||||||
repository: docker.io/org/my-app
|
repository: docker.io/org/my-app
|
||||||
@@ -73,390 +75,5 @@ image:
|
|||||||
subdomain: my-app
|
subdomain: my-app
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Core Templates
|
See ./apps/common/README.md for guide on writing charts
|
||||||
|
|
||||||
#### Deployment Template
|
|
||||||
Create `templates/deployment.yaml`:
|
|
||||||
```yaml
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}"
|
|
||||||
spec:
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
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
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: "{{ .Release.Name }}-data"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Service Template
|
|
||||||
Create `templates/service.yaml`:
|
|
||||||
```yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}"
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app: "{{ .Release.Name }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Persistent Volume Claim
|
|
||||||
Create `templates/pvc.yaml`:
|
|
||||||
```yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}-data"
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 10Gi
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Resource Definitions (CRDs)
|
|
||||||
|
|
||||||
This project uses several custom resources that are managed by operators in the cluster:
|
|
||||||
|
|
||||||
### 1. OIDC Client (OpenID Connect Authentication)
|
|
||||||
|
|
||||||
The `OidcClient` resource automatically provisions OAuth2/OIDC clients with your identity provider.
|
|
||||||
|
|
||||||
Create `templates/client.yaml`:
|
|
||||||
```yaml
|
|
||||||
apiVersion: homelab.mortenolsen.pro/v1
|
|
||||||
kind: OidcClient
|
|
||||||
metadata:
|
|
||||||
name: '{{ .Release.Name }}'
|
|
||||||
spec:
|
|
||||||
environment: '{{ .Values.globals.environment }}'
|
|
||||||
redirectUris:
|
|
||||||
- path: /oauth/oidc/callback
|
|
||||||
subdomain: '{{ .Values.subdomain }}'
|
|
||||||
matchingMode: strict
|
|
||||||
```
|
|
||||||
|
|
||||||
**What it does:**
|
|
||||||
- Creates an OIDC client in your identity provider (e.g., Authentik)
|
|
||||||
- Generates a Kubernetes secret named `{{ .Release.Name }}-client` containing:
|
|
||||||
- `clientId`: The OAuth client ID
|
|
||||||
- `clientSecret`: The OAuth client secret
|
|
||||||
- `configuration`: The OIDC provider URL
|
|
||||||
|
|
||||||
**Using in deployment:**
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
- name: OAUTH_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-client"
|
|
||||||
key: clientId
|
|
||||||
- name: OAUTH_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-client"
|
|
||||||
key: clientSecret
|
|
||||||
- name: OPENID_PROVIDER_URL
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-client"
|
|
||||||
key: configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. PostgreSQL Database
|
|
||||||
|
|
||||||
The `PostgresDatabase` resource automatically provisions PostgreSQL databases.
|
|
||||||
|
|
||||||
**New Version (Recommended):**
|
|
||||||
|
|
||||||
Create `templates/database.yaml`:
|
|
||||||
```yaml
|
|
||||||
{{ include "common.database" . }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Add to `values.yaml`:
|
|
||||||
```yaml
|
|
||||||
database:
|
|
||||||
enabled: true
|
|
||||||
```
|
|
||||||
|
|
||||||
**Legacy Version (Deprecated):**
|
|
||||||
|
|
||||||
The legacy `homelab.mortenolsen.pro/v1` API version is deprecated. For new charts, use the common library template which uses the new `postgres.homelab.mortenolsen.pro/v1` API version.
|
|
||||||
|
|
||||||
**What it does:**
|
|
||||||
- Creates a PostgreSQL database with the same name as your release
|
|
||||||
- Creates a user with appropriate permissions
|
|
||||||
- Generates a Kubernetes secret named `{{ .Release.Name }}-connection` containing:
|
|
||||||
- `url`: Complete PostgreSQL connection URL
|
|
||||||
- `host`: Database hostname
|
|
||||||
- `port`: Database port
|
|
||||||
- `database`: Database name
|
|
||||||
- `username`: Database username
|
|
||||||
- `password`: Database password
|
|
||||||
|
|
||||||
**Using in deployment:**
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
- name: DATABASE_URL
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-connection"
|
|
||||||
key: url
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** The secret name changed from `{release}-pg-connection` (legacy) to `{release}-connection` (new version). The common library template handles this automatically.
|
|
||||||
|
|
||||||
### 3. Secret Generation
|
|
||||||
|
|
||||||
The `GenerateSecret` resource creates secure random secrets.
|
|
||||||
|
|
||||||
Create `templates/secret.yaml`:
|
|
||||||
```yaml
|
|
||||||
apiVersion: homelab.mortenolsen.pro/v1
|
|
||||||
kind: GenerateSecret
|
|
||||||
metadata:
|
|
||||||
name: "{{ .Release.Name }}-secrets"
|
|
||||||
spec:
|
|
||||||
fields:
|
|
||||||
- name: encryptionkey
|
|
||||||
encoding: hex # Options: hex, base64, alphanumeric
|
|
||||||
length: 64 # Length in bytes (before encoding)
|
|
||||||
- name: apitoken
|
|
||||||
encoding: base64
|
|
||||||
length: 32
|
|
||||||
```
|
|
||||||
|
|
||||||
**What it does:**
|
|
||||||
- Generates cryptographically secure random values
|
|
||||||
- Creates a Kubernetes secret with the specified fields
|
|
||||||
- Supports different encoding formats for different use cases
|
|
||||||
|
|
||||||
**Using in deployment:**
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
- name: ENCRYPTION_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ .Release.Name }}-secrets"
|
|
||||||
key: encryptionkey
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. External HTTP Service
|
|
||||||
|
|
||||||
The `ExternalHttpService` resource configures ingress routing for your application.
|
|
||||||
|
|
||||||
Create `templates/external-http-service.yaml`:
|
|
||||||
```yaml
|
|
||||||
apiVersion: homelab.mortenolsen.pro/v1
|
|
||||||
kind: ExternalHttpService
|
|
||||||
metadata:
|
|
||||||
name: '{{ .Release.Name }}'
|
|
||||||
spec:
|
|
||||||
environment: '{{ .Values.globals.environment }}'
|
|
||||||
subdomain: '{{ .Values.subdomain }}'
|
|
||||||
destination:
|
|
||||||
host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
```
|
|
||||||
|
|
||||||
**What it does:**
|
|
||||||
- Creates ingress routes for your application
|
|
||||||
- Configures subdomain routing (e.g., `myapp.yourdomain.com`)
|
|
||||||
- Handles TLS termination automatically
|
|
||||||
- Integrates with your service mesh (if applicable)
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### 1. Naming Conventions
|
|
||||||
- Use `{{ .Release.Name }}` consistently for all resource names
|
|
||||||
- Suffix resource names appropriately: `-data`, `-secrets`, `-client`, `-database`
|
|
||||||
|
|
||||||
### 2. Container Configuration
|
|
||||||
- Always specify health checks (liveness and readiness probes)
|
|
||||||
- Use named ports (e.g., `http`, `grpc`) instead of port numbers
|
|
||||||
- Set `revisionHistoryLimit: 0` to prevent accumulation of old ReplicaSets
|
|
||||||
|
|
||||||
### 3. Environment Variables
|
|
||||||
- Never hardcode secrets in values.yaml
|
|
||||||
- Use secretKeyRef to reference generated secrets
|
|
||||||
- Group related environment variables together
|
|
||||||
|
|
||||||
### 4. Persistent Storage
|
|
||||||
- Always use PVCs for stateful data
|
|
||||||
- Consider storage requirements carefully (start with reasonable defaults)
|
|
||||||
- Mount data at standard paths for the application
|
|
||||||
|
|
||||||
### 5. OIDC Integration
|
|
||||||
- Set `ENABLE_SIGNUP: "false"` if using OIDC
|
|
||||||
- Enable OIDC signup with `ENABLE_OAUTH_SIGNUP: "true"`
|
|
||||||
- Configure email merging if needed with `OAUTH_MERGE_ACCOUNTS_BY_EMAIL`
|
|
||||||
|
|
||||||
### 6. Database Usage
|
|
||||||
- Only include database.yaml if the app needs PostgreSQL
|
|
||||||
- Applications should support DATABASE_URL environment variable
|
|
||||||
- Consider connection pooling settings for production
|
|
||||||
|
|
||||||
## Disabling Applications
|
|
||||||
|
|
||||||
To temporarily disable an application, rename its directory with `.disabled` suffix:
|
|
||||||
```bash
|
|
||||||
mv apps/charts/my-app apps/charts/my-app.disabled
|
|
||||||
```
|
|
||||||
|
|
||||||
The ArgoCD ApplicationSet will automatically exclude directories matching `*.disabled`.
|
|
||||||
|
|
||||||
## Testing Your Chart
|
|
||||||
|
|
||||||
1. **Lint your chart:**
|
|
||||||
```bash
|
|
||||||
helm lint apps/charts/my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Render templates locally:**
|
|
||||||
```bash
|
|
||||||
helm template my-app apps/charts/my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Dry run installation:**
|
|
||||||
```bash
|
|
||||||
helm install my-app apps/charts/my-app --dry-run --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deployment Workflow
|
|
||||||
|
|
||||||
**IMPORTANT:** There is no test environment. When creating or modifying applications:
|
|
||||||
|
|
||||||
1. **Make changes directly to the files** - The agent will write changes to the actual chart files
|
|
||||||
2. **User deploys the changes** - After changes are made, the user must deploy them to the cluster
|
|
||||||
3. **Debug with kubectl** - If issues arise after deployment, agents can use kubectl to:
|
|
||||||
- Check pod status and logs
|
|
||||||
- Inspect generated resources
|
|
||||||
- Verify secret creation
|
|
||||||
- Troubleshoot configuration issues
|
|
||||||
|
|
||||||
**Note:** Agents cannot deploy applications themselves. They can only:
|
|
||||||
- Create and modify chart files
|
|
||||||
- Use kubectl to investigate deployment issues
|
|
||||||
- Provide debugging assistance and recommendations
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
### Application with OIDC + Database
|
|
||||||
For apps requiring both authentication and database:
|
|
||||||
- Include `client.yaml` for OIDC
|
|
||||||
- Include `database.yaml` for PostgreSQL
|
|
||||||
- Reference both secrets in deployment
|
|
||||||
|
|
||||||
### Stateless Applications
|
|
||||||
For simple stateless apps:
|
|
||||||
- Omit `pvc.yaml`
|
|
||||||
- Remove volume mounts from deployment
|
|
||||||
- Consider using `Deployment` scaling if appropriate
|
|
||||||
|
|
||||||
### Background Services
|
|
||||||
For services without web interface:
|
|
||||||
- Omit `external-http-service.yaml`
|
|
||||||
- Omit `client.yaml` (no OIDC needed)
|
|
||||||
- Focus on service discovery within cluster
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Secret Not Found
|
|
||||||
If secrets are not being created:
|
|
||||||
1. Check that the CRD controller is running
|
|
||||||
2. Verify the `environment` value matches your setup
|
|
||||||
3. Check controller logs for provisioning errors
|
|
||||||
|
|
||||||
### OIDC Issues
|
|
||||||
1. Verify redirect URIs match exactly
|
|
||||||
2. Check that the identity provider is accessible
|
|
||||||
3. Ensure the client secret is being properly mounted
|
|
||||||
|
|
||||||
### Database Connection
|
|
||||||
1. Verify the database operator is running
|
|
||||||
2. Check network policies between namespaces
|
|
||||||
3. Ensure the database server has capacity
|
|
||||||
|
|
||||||
## Global Values
|
|
||||||
|
|
||||||
Applications can access global values through `{{ .Values.globals }}`:
|
|
||||||
- `environment`: The deployment environment (e.g., "production", "staging")
|
|
||||||
- Additional values can be added at the root chart level
|
|
||||||
|
|
||||||
## Maintenance
|
|
||||||
|
|
||||||
### Updating Images
|
|
||||||
1. Update the tag in `values.yaml`:
|
|
||||||
```yaml
|
|
||||||
tag: v1.0.0 # Use semantic version tags only
|
|
||||||
```
|
|
||||||
2. **Note:** Do not include SHA digests in tags. Immutable digests are automatically added later by Renovate
|
|
||||||
|
|
||||||
### Renovate Integration
|
|
||||||
The project uses Renovate for automated dependency updates. Configure in `renovate.json5` to:
|
|
||||||
- Auto-update container images
|
|
||||||
- Create pull requests for updates
|
|
||||||
- Group related updates
|
|
||||||
|
|
||||||
### Backup Considerations
|
|
||||||
For applications with persistent data:
|
|
||||||
1. Consider implementing backup CronJobs
|
|
||||||
2. Use volume snapshots if available
|
|
||||||
3. Export data regularly for critical applications
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
When adding new applications:
|
|
||||||
1. Follow the existing patterns and conventions
|
|
||||||
2. Document any special requirements in the chart's README
|
|
||||||
3. Consider security implications of all configurations
|
|
||||||
4. Update this document if introducing new patterns
|
|
||||||
|
|
||||||
## Maintaining This Document
|
|
||||||
|
|
||||||
**IMPORTANT:** When making changes to the project structure, patterns, or custom resources:
|
|
||||||
- Keep this AGENTS.md file up to date with any changes
|
|
||||||
- Document new CRDs or custom resources as they are added
|
|
||||||
- Update examples if the patterns change
|
|
||||||
- Add new sections for significant new features or patterns
|
|
||||||
- Ensure all code examples remain accurate and tested
|
|
||||||
|
|
||||||
This document serves as the primary reference for creating and maintaining applications in this project. Keeping it current ensures consistency and helps onboard new contributors.
|
|
||||||
@@ -525,6 +525,56 @@ externalSecrets:
|
|||||||
|
|
||||||
The secret will contain a key named `mySecretKey` (not `my-password-generator`).
|
The secret will contain a key named `mySecretKey` (not `my-password-generator`).
|
||||||
|
|
||||||
|
## Secret-based Configurations
|
||||||
|
|
||||||
|
When an application requires a configuration file (like `config.yaml` or `.env`) that contains sensitive data, you should generate a `Secret` using External Secrets templating instead of a `ConfigMap`. This keeps the sensitive data encrypted in ETCD while allowing you to merge static and dynamic values.
|
||||||
|
|
||||||
|
### 1. Create an External Secret with a Template
|
||||||
|
Create a custom template in your chart (e.g., `templates/external-config.yaml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: external-secrets.io/v1beta1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "common.fullname" . }}-config
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
name: vault-backend # Replace with your SecretStore name
|
||||||
|
kind: SecretStore
|
||||||
|
target:
|
||||||
|
name: {{ include "common.fullname" . }}-config
|
||||||
|
template:
|
||||||
|
engineVersion: v2
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
database:
|
||||||
|
user: {{ "{{" }} .db_user | toString {{ "}}" }}
|
||||||
|
password: {{ "{{" }} .db_pass | toString {{ "}}" }}
|
||||||
|
data:
|
||||||
|
- secretKey: db_user
|
||||||
|
remoteRef:
|
||||||
|
key: database/credentials
|
||||||
|
property: username
|
||||||
|
- secretKey: db_pass
|
||||||
|
remoteRef:
|
||||||
|
key: database/credentials
|
||||||
|
property: password
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Mount the Secret in values.yaml
|
||||||
|
Reference the generated secret in your `volumes` configuration using the `{release}` placeholder:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
mountPath: /app/config.yaml
|
||||||
|
subPath: config.yaml
|
||||||
|
secret: "{release}-config"
|
||||||
|
```
|
||||||
|
|
||||||
## Placeholders
|
## Placeholders
|
||||||
|
|
||||||
Use placeholders in `values.yaml` for dynamic values that are resolved at template time:
|
Use placeholders in `values.yaml` for dynamic values that are resolved at template time:
|
||||||
|
|||||||
@@ -214,10 +214,10 @@ Standard volumes
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- else if .configMap }}
|
{{- else if .configMap }}
|
||||||
configMap:
|
configMap:
|
||||||
name: {{ .configMap }}
|
name: {{ .configMap | replace "{release}" $.Release.Name | replace "{namespace}" $.Release.Namespace | replace "{fullname}" (include "common.fullname" $) }}
|
||||||
{{- else if .secret }}
|
{{- else if .secret }}
|
||||||
secret:
|
secret:
|
||||||
secretName: {{ .secret }}
|
secretName: {{ .secret | replace "{release}" $.Release.Name | replace "{namespace}" $.Release.Namespace | replace "{fullname}" (include "common.fullname" $) }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
Reference in New Issue
Block a user