mirror of
https://github.com/lukaszraczylo/kubemirror.git
synced 2026-07-01 12:55:13 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
# KubeMirror Examples
|
||||
|
||||
This directory contains example manifests for testing KubeMirror functionality.
|
||||
|
||||
## Overview
|
||||
|
||||
The examples create 5 namespaces with various resources to demonstrate different mirroring scenarios:
|
||||
|
||||
### Namespace Structure
|
||||
|
||||
- **namespace-1**: Source namespace containing:
|
||||
- `shared-credentials` Secret → mirrors to ALL namespaces
|
||||
- `database-credentials` Secret → mirrors to namespace-3 and namespace-4
|
||||
- `local-secret` Secret → NO mirroring (stays local)
|
||||
- `app-config` ConfigMap → mirrors to ALL namespaces
|
||||
- `nginx-config` ConfigMap → mirrors to namespace-2 and namespace-5
|
||||
|
||||
- **namespace-2**: Traefik middleware source namespace containing:
|
||||
- `compression` Middleware → mirrors to namespace-4 and namespace-5
|
||||
- `rate-limit` Middleware → mirrors to ALL namespaces
|
||||
- `headers` Middleware → mirrors to namespace-3 only
|
||||
|
||||
- **namespace-3**: Target namespace (receives mirrors)
|
||||
- **namespace-4**: Target namespace (receives mirrors + Traefik middleware)
|
||||
- **namespace-5**: Target namespace (receives mirrors + Traefik middleware)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. KubeMirror controller must be deployed and running
|
||||
2. Traefik CRDs must be installed (for middleware examples)
|
||||
|
||||
```bash
|
||||
# Install official Traefik CRDs (latest)
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/master/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
|
||||
```
|
||||
|
||||
**Note:** If you don't want to test Traefik middleware mirroring, you can skip the CRD installation and just exclude `traefik-middleware.yaml` from your apply command.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Apply all examples using kustomize:
|
||||
|
||||
```bash
|
||||
# Apply all examples
|
||||
kubectl apply -k examples/
|
||||
|
||||
# Or apply individually
|
||||
kubectl apply -f examples/namespaces.yaml
|
||||
kubectl apply -f examples/source-secret.yaml
|
||||
kubectl apply -f examples/source-configmap.yaml
|
||||
kubectl apply -f examples/traefik-middleware.yaml
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Check Namespaces
|
||||
|
||||
```bash
|
||||
# List all example namespaces
|
||||
kubectl get namespaces -l app=kubemirror-example
|
||||
|
||||
# Verify allow-mirrors label
|
||||
kubectl get namespaces -l kubemirror.raczylo.com/allow-mirrors=true
|
||||
```
|
||||
|
||||
### Check Mirrored Secrets
|
||||
|
||||
```bash
|
||||
# Check shared-credentials (should exist in all namespaces)
|
||||
kubectl get secret shared-credentials -n namespace-1
|
||||
kubectl get secret shared-credentials -n namespace-2
|
||||
kubectl get secret shared-credentials -n namespace-3
|
||||
kubectl get secret shared-credentials -n namespace-4
|
||||
kubectl get secret shared-credentials -n namespace-5
|
||||
|
||||
# Check database-credentials (only in namespace-3 and namespace-4)
|
||||
kubectl get secret database-credentials -n namespace-3
|
||||
kubectl get secret database-credentials -n namespace-4
|
||||
|
||||
# Check local-secret (should ONLY exist in namespace-1)
|
||||
kubectl get secret local-secret -n namespace-1
|
||||
kubectl get secret local-secret -n namespace-2 # Should NOT exist
|
||||
```
|
||||
|
||||
### Check Mirrored ConfigMaps
|
||||
|
||||
```bash
|
||||
# Check app-config (should exist in all namespaces)
|
||||
kubectl get configmap app-config --all-namespaces
|
||||
|
||||
# Check nginx-config (only in namespace-2 and namespace-5)
|
||||
kubectl get configmap nginx-config -n namespace-2
|
||||
kubectl get configmap nginx-config -n namespace-5
|
||||
```
|
||||
|
||||
### Check Mirrored Traefik Middlewares
|
||||
|
||||
```bash
|
||||
# Check compression middleware (should be in namespace-4 and namespace-5)
|
||||
kubectl get middleware compression -n namespace-2
|
||||
kubectl get middleware compression -n namespace-4
|
||||
kubectl get middleware compression -n namespace-5
|
||||
|
||||
# Check rate-limit middleware (should be in all namespaces)
|
||||
kubectl get middleware rate-limit --all-namespaces
|
||||
|
||||
# Check headers middleware (should be in namespace-3)
|
||||
kubectl get middleware headers -n namespace-3
|
||||
```
|
||||
|
||||
### Check Mirror Ownership
|
||||
|
||||
Verify that mirrored resources have the correct ownership labels:
|
||||
|
||||
```bash
|
||||
# Check labels on a mirrored secret
|
||||
kubectl get secret shared-credentials -n namespace-3 -o yaml | grep -A 5 labels
|
||||
|
||||
# Should include:
|
||||
# kubemirror.raczylo.com/mirrored: "true"
|
||||
# kubemirror.raczylo.com/source-namespace: namespace-1
|
||||
# kubemirror.raczylo.com/source-name: shared-credentials
|
||||
```
|
||||
|
||||
## Testing Update Propagation
|
||||
|
||||
Test that updates to source resources propagate to mirrors:
|
||||
|
||||
```bash
|
||||
# Update the shared-credentials secret
|
||||
kubectl patch secret shared-credentials -n namespace-1 \
|
||||
--type='json' \
|
||||
-p='[{"op": "replace", "path": "/data/password", "value": "'$(echo -n "new-password" | base64)'"}]'
|
||||
|
||||
# Wait a few seconds, then verify the change propagated
|
||||
kubectl get secret shared-credentials -n namespace-3 -o jsonpath='{.data.password}' | base64 -d
|
||||
# Should output: new-password
|
||||
```
|
||||
|
||||
## Testing Deletion Behavior
|
||||
|
||||
Test that deleting source resources deletes mirrors:
|
||||
|
||||
```bash
|
||||
# Delete a source secret
|
||||
kubectl delete secret database-credentials -n namespace-1
|
||||
|
||||
# Wait a few seconds, verify mirrors are also deleted
|
||||
kubectl get secret database-credentials -n namespace-3 # Should not exist
|
||||
kubectl get secret database-credentials -n namespace-4 # Should not exist
|
||||
```
|
||||
|
||||
Test that deleting a mirror recreates it (if source still exists):
|
||||
|
||||
```bash
|
||||
# Delete a mirrored resource
|
||||
kubectl delete secret shared-credentials -n namespace-4
|
||||
|
||||
# Wait a few seconds, verify it's recreated
|
||||
kubectl get secret shared-credentials -n namespace-4 # Should exist again
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
|
||||
Remove all examples:
|
||||
|
||||
```bash
|
||||
# Delete all resources
|
||||
kubectl delete -k examples/
|
||||
|
||||
# Or delete individually
|
||||
kubectl delete -f examples/traefik-middleware.yaml
|
||||
kubectl delete -f examples/source-configmap.yaml
|
||||
kubectl delete -f examples/source-secret.yaml
|
||||
kubectl delete -f examples/namespaces.yaml
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### View KubeMirror Logs
|
||||
|
||||
```bash
|
||||
# View controller logs
|
||||
kubectl logs -n kubemirror-system -l app.kubernetes.io/name=kubemirror -f
|
||||
```
|
||||
|
||||
### Check Controller Events
|
||||
|
||||
```bash
|
||||
# View events in a specific namespace
|
||||
kubectl get events -n namespace-3 --sort-by='.lastTimestamp'
|
||||
|
||||
# Look for mirror-related events
|
||||
kubectl get events --all-namespaces | grep -i mirror
|
||||
```
|
||||
|
||||
### Verify Controller is Running
|
||||
|
||||
```bash
|
||||
# Check controller deployment
|
||||
kubectl get deployment -n kubemirror-system
|
||||
|
||||
# Check controller pods
|
||||
kubectl get pods -n kubemirror-system
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Mirrors not created**: Ensure target namespaces have the `kubemirror.raczylo.com/allow-mirrors: "true"` label
|
||||
2. **Updates not propagating**: Check controller logs for errors or rate limiting
|
||||
3. **Traefik resources not mirroring**: Ensure Traefik CRDs are installed in the cluster
|
||||
4. **Permission errors**: Verify the controller has proper RBAC permissions
|
||||
|
||||
## Advanced Examples
|
||||
|
||||
### Mirror to All Except Specific Namespaces
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: almost-all
|
||||
namespace: namespace-1
|
||||
annotations:
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "all"
|
||||
kubemirror.raczylo.com/excluded-namespaces: "namespace-3"
|
||||
labels:
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
data:
|
||||
key: dmFsdWU= # "value" in base64
|
||||
```
|
||||
|
||||
### Pattern-Based Mirroring
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: app-config
|
||||
namespace: namespace-1
|
||||
annotations:
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "all"
|
||||
kubemirror.raczylo.com/namespace-pattern: "app-.*"
|
||||
labels:
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
data:
|
||||
config: "value"
|
||||
```
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# Example: Mirror a ConfigMap to all namespaces (except excluded ones)
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: app-config
|
||||
namespace: default
|
||||
labels:
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
annotations:
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
# Mirror to all namespaces (except kube-system, kube-public, etc.)
|
||||
kubemirror.raczylo.com/target-namespaces: "all"
|
||||
data:
|
||||
app.conf: |
|
||||
server {
|
||||
listen 8080;
|
||||
location / {
|
||||
proxy_pass http://backend:3000;
|
||||
}
|
||||
}
|
||||
log-level: "info"
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- namespaces.yaml
|
||||
- https://raw.githubusercontent.com/traefik/traefik/master/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
|
||||
- source-secret.yaml
|
||||
- source-configmap.yaml
|
||||
- traefik-middleware.yaml
|
||||
|
||||
commonLabels:
|
||||
managed-by: kustomize
|
||||
example: kubemirror
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-1
|
||||
labels:
|
||||
# This namespace contains source resources
|
||||
kubemirror.raczylo.com/allow-mirrors: "true"
|
||||
app: kubemirror-example
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-2
|
||||
labels:
|
||||
# This namespace contains Traefik middleware
|
||||
kubemirror.raczylo.com/allow-mirrors: "true"
|
||||
app: kubemirror-example
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-3
|
||||
labels:
|
||||
# This namespace will receive mirrored resources
|
||||
kubemirror.raczylo.com/allow-mirrors: "true"
|
||||
app: kubemirror-example
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-4
|
||||
labels:
|
||||
# This namespace will receive all mirrors + Traefik middleware
|
||||
kubemirror.raczylo.com/allow-mirrors: "true"
|
||||
app: kubemirror-example
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-5
|
||||
labels:
|
||||
# This namespace will receive all mirrors + Traefik middleware
|
||||
kubemirror.raczylo.com/allow-mirrors: "true"
|
||||
app: kubemirror-example
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# Example: Mirror a secret to specific namespaces
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: my-app-secret
|
||||
namespace: default
|
||||
labels:
|
||||
# REQUIRED: Enable mirroring with label (for server-side filtering)
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
annotations:
|
||||
# REQUIRED: Sync annotation
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
# REQUIRED: Target namespaces (comma-separated)
|
||||
kubemirror.raczylo.com/target-namespaces: "app1,app2,app3"
|
||||
type: Opaque
|
||||
data:
|
||||
username: YWRtaW4= # admin
|
||||
password: cGFzc3dvcmQxMjM= # password123
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
# Example: Mirror a secret to all namespaces matching a pattern
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: tls-cert
|
||||
namespace: default
|
||||
labels:
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
annotations:
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
# Mirror to all namespaces starting with "app-"
|
||||
kubemirror.raczylo.com/target-namespaces: "app-*"
|
||||
type: kubernetes.io/tls
|
||||
data:
|
||||
tls.crt: LS0tLS1CRUdJTi... # Base64 encoded cert
|
||||
tls.key: LS0tLS1CRUdJTi... # Base64 encoded key
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: app-config
|
||||
namespace: namespace-1
|
||||
annotations:
|
||||
# Mirror this ConfigMap to all namespaces
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "all"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: shared-config
|
||||
data:
|
||||
app.properties: |
|
||||
server.port=8080
|
||||
server.host=0.0.0.0
|
||||
log.level=info
|
||||
feature.enabled=true
|
||||
|
||||
database.yaml: |
|
||||
database:
|
||||
pool:
|
||||
min: 5
|
||||
max: 20
|
||||
timeout: 30s
|
||||
|
||||
settings.json: |
|
||||
{
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"timezone": "UTC",
|
||||
"features": {
|
||||
"beta": false,
|
||||
"experimental": false
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: nginx-config
|
||||
namespace: namespace-1
|
||||
annotations:
|
||||
# Mirror to specific namespaces only
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "namespace-2,namespace-5"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: nginx-config
|
||||
data:
|
||||
nginx.conf: |
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
gzip on;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: shared-credentials
|
||||
namespace: namespace-1
|
||||
annotations:
|
||||
# Mirror this secret to all namespaces with allow-mirrors label
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "all"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: shared-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: admin
|
||||
password: super-secret-password
|
||||
api-key: "1234567890abcdef"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: database-credentials
|
||||
namespace: namespace-1
|
||||
annotations:
|
||||
# Mirror this secret only to specific namespaces
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "namespace-3,namespace-4"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: database-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
db-host: "postgres.example.com"
|
||||
db-user: "appuser"
|
||||
db-password: "db-secret-password"
|
||||
db-name: "myapp"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: local-secret
|
||||
namespace: namespace-1
|
||||
labels:
|
||||
app: kubemirror-example
|
||||
resource-type: local-only
|
||||
type: Opaque
|
||||
stringData:
|
||||
# This secret has NO mirror annotation, so it stays local
|
||||
local-data: "This stays in namespace-1 only"
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: compression
|
||||
namespace: namespace-2
|
||||
annotations:
|
||||
# Mirror this Traefik middleware to namespace-4 and namespace-5
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "namespace-4,namespace-5"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: traefik-middleware
|
||||
spec:
|
||||
compress:
|
||||
excludedContentTypes:
|
||||
- text/event-stream
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: rate-limit
|
||||
namespace: namespace-2
|
||||
annotations:
|
||||
# Mirror to all namespaces for consistent rate limiting
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "all"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: traefik-middleware
|
||||
spec:
|
||||
rateLimit:
|
||||
average: 100
|
||||
burst: 50
|
||||
period: 1m
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: headers
|
||||
namespace: namespace-2
|
||||
annotations:
|
||||
# Mirror only to namespace-3
|
||||
kubemirror.raczylo.com/sync: "true"
|
||||
kubemirror.raczylo.com/target-namespaces: "namespace-3"
|
||||
labels:
|
||||
# Required: enables server-side filtering
|
||||
kubemirror.raczylo.com/enabled: "true"
|
||||
app: kubemirror-example
|
||||
resource-type: traefik-middleware
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
X-Forwarded-Proto: "https"
|
||||
X-Frame-Options: "DENY"
|
||||
X-Content-Type-Options: "nosniff"
|
||||
customResponseHeaders:
|
||||
X-Custom-Response-Header: "kubemirror-example"
|
||||
Reference in New Issue
Block a user