feat: Complete Kustomize migration with environment variable integration
🎉 Major enhancement: Full migration from shell script deployment to Kustomize
## New Features
### Kustomize Infrastructure
- ✅ Complete base resources for all Kubernetes manifests
- ✅ Development overlay with optimized dev settings
- ✅ Production overlay with enterprise-grade security and performance
- ✅ ConfigMap and Secret generation from environment variables
- ✅ Image tag and replica management per environment
### Environment Variable Integration
- ✅ Multi-source environment loading (~/.env, .env.dev, .env.prod, .env.local)
- ✅ Static configuration generation from environment variables
- ✅ Dynamic runtime environment variable injection
- ✅ Comprehensive variable documentation and examples
- ✅ Secrets template generation for secure credential management
### Enhanced Makefile
- ✅ 20+ new Kustomize-specific deployment targets
- ✅ Environment-aware configuration generation commands
- ✅ Validation, dry-run, and debugging capabilities
- ✅ Backward compatibility with legacy shell script deployment
### New Scripts & Tools
- ✅ scripts/generate-config.sh - Environment variable to Kustomize config generator
- ✅ scripts/deploy-with-env.sh - Runtime environment variable deployment tool
- ✅ Comprehensive help and usage documentation
### Documentation
- ✅ k8s-kustomize/README.md - Complete Kustomize deployment guide
- ✅ docs/ENVIRONMENT_VARIABLES.md - Environment variable integration guide
- ✅ KUSTOMIZE_MIGRATION.md - Migration summary and next steps
## Benefits
- 🚀 Simplified deployment: make deploy-dev vs complex shell scripts
- 🔒 Environment isolation: Clear dev/staging/prod separation
- 🔧 GitOps ready: Works seamlessly with ArgoCD, Flux
- ✅ Better validation: Built-in YAML validation catches errors early
- 📈 Standard approach: Industry-standard Kubernetes deployment method
- 🛡️ Enhanced security: Production security contexts, network policies, TLS
## Usage Examples
[34mGenerating development configuration...[0m
[0;34m[INFO][0m Kustomize Config Generator
[0;34m[INFO][0m Environment: dev
[0;34m[INFO][0m Loading environment variables...
[1;33m[WARNING][0m File not found: /home/will/.env
[0;34m[INFO][0m Loading: /home/will/Code/meds/.env
[1;33m[WARNING][0m File not found: /home/will/Code/meds/.env.dev
[1;33m[WARNING][0m File not found: /home/will/Code/meds/.env.local
[0;34m[INFO][0m Generating base config.env...
[0;32m[SUCCESS][0m Generated: /home/will/Code/meds/k8s-kustomize/base/config.env
[0;34m[INFO][0m Generating environment-specific config for: dev
[0;32m[SUCCESS][0m Generated development config: /home/will/Code/meds/k8s-kustomize/overlays/dev/config.env
[0;34m[INFO][0m Validating generated configuration...
[0;32m[SUCCESS][0m Configuration validation passed!
[0;32m[SUCCESS][0m Configuration generation completed!
[0;34m[INFO][0m Next steps:
1. Review generated files in k8s-kustomize/
2. Update any environment-specific values
3. Create secrets.env files for sensitive data
4. Test with: make kustomize-dry-run-dev
[34mDeploying to Kubernetes with Kustomize (dev)...[0m
[34mDeploying to production with environment variables...[0m
[0;34m[INFO][0m Kustomize Deployment with Environment Variables
[0;34m[INFO][0m Environment: prod
[0;34m[INFO][0m Action: apply
[0;34m[INFO][0m Validating prerequisites...
[0;32m[SUCCESS][0m Prerequisites validated
[0;34m[INFO][0m Loading environment variables for: prod
[0;34m[INFO][0m Loading: /home/will/Code/meds/.env
[0;32m[SUCCESS][0m Environment loaded: prod
[0;34m[INFO][0m Key variables:
APP_NAME: rxminder
NODE_ENV: production
IMAGE_TAG: latest
NAMESPACE: rxminder-prod
INGRESS_HOST: rxminder.192.168.153.243.nip.io
[0;34m[INFO][0m Generating dynamic configuration...
[34mValidating Kustomize configuration (dev)...[0m
configmap/rxminder-config-4229dg76t6 created (dry run)
secret/couchdb-secret-7ck2cc96g5 created (dry run)
service/rxminder-couchdb-service created (dry run)
service/rxminder-frontend-service created (dry run)
persistentvolumeclaim/rxminder-couchdb-pvc created (dry run)
deployment.apps/rxminder-frontend created (dry run)
statefulset.apps/rxminder-couchdb created (dry run)
horizontalpodautoscaler.autoscaling/rxminder-frontend-hpa created (dry run)
job.batch/rxminder-db-seed created (dry run)
ingress.networking.k8s.io/rxminder-ingress created (dry run)
networkpolicy.networking.k8s.io/rxminder-database-policy created (dry run)
networkpolicy.networking.k8s.io/rxminder-frontend-policy created (dry run)
[34mValidating Kustomize configuration (prod)...[0m
configmap/rxminder-config-2979gkcf9c created (dry run)
secret/couchdb-secret-6k9794bgg2 created (dry run)
service/rxminder-couchdb-service created (dry run)
service/rxminder-frontend-service created (dry run)
persistentvolumeclaim/rxminder-couchdb-pvc created (dry run)
deployment.apps/rxminder-frontend created (dry run)
statefulset.apps/rxminder-couchdb created (dry run)
horizontalpodautoscaler.autoscaling/rxminder-frontend-hpa created (dry run)
job.batch/rxminder-db-seed created (dry run)
ingress.networking.k8s.io/rxminder-ingress created (dry run)
networkpolicy.networking.k8s.io/rxminder-database-policy created (dry run)
networkpolicy.networking.k8s.io/rxminder-frontend-policy created (dry run)
[32mKustomize validation completed![0m
[34mDry run Kustomize deployment (dev)...[0m
apiVersion: v1
items:
- apiVersion: v1
data:
APP_NAME: rxminder
APP_VERSION: 1.0.0
CACHE_TTL: "1800"
CERT_MANAGER_ISSUER: letsencrypt-prod
CORS_ORIGIN: '*'
COUCHDB_DATABASE_NAME: meds_app
DB_HOST: rxminder-couchdb-service
DB_PORT: "5984"
DEBUG: "true"
DEV_MODE: "false"
ENABLE_CORS: "true"
ENABLE_METRICS: "false"
ENABLE_MONITORING: "false"
ENABLE_TRACING: "false"
HEALTH_CHECK_INTERVAL: "30"
HOT_RELOAD: "false"
IMAGE_REPOSITORY: will/rxminder
INGRESS_CLASS: nginx
LOG_FORMAT: json
LOG_LEVEL: debug
LOG_TIMESTAMP: "true"
MAX_CONNECTIONS: "100"
METRICS_PORT: "9090"
NODE_ENV: development
REACT_APP_API_URL: http://rxminder-couchdb-service:5984
READINESS_CHECK_TIMEOUT: "5"
REGISTRY_URL: gitea-http.taildb3494.ts.net
REQUEST_TIMEOUT: "30000"
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"APP_NAME":"rxminder","APP_VERSION":"1.0.0","CACHE_TTL":"1800","CERT_MANAGER_ISSUER":"letsencrypt-prod","CORS_ORIGIN":"*","COUCHDB_DATABASE_NAME":"meds_app","DB_HOST":"rxminder-couchdb-service","DB_PORT":"5984","DEBUG":"true","DEV_MODE":"false","ENABLE_CORS":"true","ENABLE_METRICS":"false","ENABLE_MONITORING":"false","ENABLE_TRACING":"false","HEALTH_CHECK_INTERVAL":"30","HOT_RELOAD":"false","IMAGE_REPOSITORY":"will/rxminder","INGRESS_CLASS":"nginx","LOG_FORMAT":"json","LOG_LEVEL":"debug","LOG_TIMESTAMP":"true","MAX_CONNECTIONS":"100","METRICS_PORT":"9090","NODE_ENV":"development","REACT_APP_API_URL":"http://rxminder-couchdb-service:5984","READINESS_CHECK_TIMEOUT":"5","REGISTRY_URL":"gitea-http.taildb3494.ts.net","REQUEST_TIMEOUT":"30000"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"rxminder","environment":"dev","version":"v1.0.0"},"name":"rxminder-config-4229dg76t6","namespace":"rxminder-dev"}}
labels:
app: rxminder
environment: dev
version: v1.0.0
name: rxminder-config-4229dg76t6
namespace: rxminder-dev
- apiVersion: v1
data:
password: ZGV2cGFzczEyMw==
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"password":"ZGV2cGFzczEyMw==","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"labels":{"app":"rxminder","environment":"dev","version":"v1.0.0"},"name":"couchdb-secret-7ck2cc96g5","namespace":"rxminder-dev"},"type":"Opaque"}
labels:
app: rxminder
environment: dev
version: v1.0.0
name: couchdb-secret-7ck2cc96g5
namespace: rxminder-dev
type: Opaque
- apiVersion: v1
kind: Service
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-couchdb-service","namespace":"rxminder-dev"},"spec":{"ports":[{"name":"couchdb","port":5984,"protocol":"TCP","targetPort":5984}],"selector":{"app":"rxminder","component":"database"},"type":"ClusterIP"}}
labels:
app: rxminder
component: database
environment: dev
version: v1.0.0
name: rxminder-couchdb-service
namespace: rxminder-dev
spec:
ports:
- name: couchdb
port: 5984
protocol: TCP
targetPort: 5984
selector:
app: rxminder
component: database
type: ClusterIP
- apiVersion: v1
kind: Service
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend-service","namespace":"rxminder-dev"},"spec":{"ports":[{"name":"http","port":80,"protocol":"TCP","targetPort":80}],"selector":{"app":"rxminder","component":"frontend"},"type":"ClusterIP"}}
labels:
app: rxminder
component: frontend
environment: dev
version: v1.0.0
name: rxminder-frontend-service
namespace: rxminder-dev
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: rxminder
component: frontend
type: ClusterIP
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-couchdb-pvc","namespace":"rxminder-dev"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"1Gi"}},"storageClassName":"standard"}}
labels:
app: rxminder
component: database
environment: dev
version: v1.0.0
name: rxminder-couchdb-pvc
namespace: rxminder-dev
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard
- apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend","namespace":"rxminder-dev"},"spec":{"replicas":1,"selector":{"matchLabels":{"component":"frontend"}},"template":{"metadata":{"labels":{"component":"frontend"}},"spec":{"containers":[{"env":[{"name":"NODE_ENV","value":"development"},{"name":"LOG_LEVEL","value":"debug"}],"envFrom":[{"configMapRef":{"name":"rxminder-config-4229dg76t6"}}],"image":"gitea-http.taildb3494.ts.net/will/rxminder:dev","livenessProbe":{"httpGet":{"path":"/","port":80},"initialDelaySeconds":30,"periodSeconds":30},"name":"frontend","ports":[{"containerPort":80}],"readinessProbe":{"httpGet":{"path":"/","port":80},"initialDelaySeconds":5,"periodSeconds":5},"resources":{"limits":{"cpu":"40m","memory":"32Mi"},"requests":{"cpu":"20m","memory":"16Mi"}}}],"imagePullSecrets":[{"name":"rxminder-registry-secret"}]}}}}
labels:
app: rxminder
component: frontend
environment: dev
version: v1.0.0
name: rxminder-frontend
namespace: rxminder-dev
spec:
replicas: 1
selector:
matchLabels:
component: frontend
template:
metadata:
labels:
component: frontend
spec:
containers:
- env:
- name: NODE_ENV
value: development
- name: LOG_LEVEL
value: debug
envFrom:
- configMapRef:
name: rxminder-config-4229dg76t6
image: gitea-http.taildb3494.ts.net/will/rxminder:dev
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 30
name: frontend
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
resources:
limits:
cpu: 40m
memory: 32Mi
requests:
cpu: 20m
memory: 16Mi
imagePullSecrets:
- name: rxminder-registry-secret
- apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-couchdb","namespace":"rxminder-dev"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"rxminder","component":"database"}},"serviceName":"rxminder-couchdb-service","template":{"metadata":{"labels":{"app":"rxminder","component":"database"}},"spec":{"containers":[{"env":[{"name":"COUCHDB_USER","valueFrom":{"secretKeyRef":{"key":"username","name":"couchdb-secret-7ck2cc96g5"}}},{"name":"COUCHDB_PASSWORD","valueFrom":{"secretKeyRef":{"key":"password","name":"couchdb-secret-7ck2cc96g5"}}}],"image":"couchdb:3.3.2","livenessProbe":{"httpGet":{"path":"/_up","port":5984},"initialDelaySeconds":60,"periodSeconds":30},"name":"couchdb","ports":[{"containerPort":5984}],"readinessProbe":{"httpGet":{"path":"/_up","port":5984},"initialDelaySeconds":10,"periodSeconds":5},"resources":{"limits":{"cpu":"60m","memory":"128Mi"},"requests":{"cpu":"30m","memory":"64Mi"}},"volumeMounts":[{"mountPath":"/opt/couchdb/data","name":"couchdb-data"}]}]}},"volumeClaimTemplates":[{"metadata":{"labels":{"app":"rxminder","component":"database"},"name":"couchdb-data"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"1Gi"}},"storageClassName":"standard"}}]}}
labels:
app: rxminder
component: database
environment: dev
version: v1.0.0
name: rxminder-couchdb
namespace: rxminder-dev
spec:
replicas: 1
selector:
matchLabels:
app: rxminder
component: database
serviceName: rxminder-couchdb-service
template:
metadata:
labels:
app: rxminder
component: database
spec:
containers:
- env:
- name: COUCHDB_USER
valueFrom:
secretKeyRef:
key: username
name: couchdb-secret-7ck2cc96g5
- name: COUCHDB_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: couchdb-secret-7ck2cc96g5
image: couchdb:3.3.2
livenessProbe:
httpGet:
path: /_up
port: 5984
initialDelaySeconds: 60
periodSeconds: 30
name: couchdb
ports:
- containerPort: 5984
readinessProbe:
httpGet:
path: /_up
port: 5984
initialDelaySeconds: 10
periodSeconds: 5
resources:
limits:
cpu: 60m
memory: 128Mi
requests:
cpu: 30m
memory: 64Mi
volumeMounts:
- mountPath: /opt/couchdb/data
name: couchdb-data
volumeClaimTemplates:
- metadata:
labels:
app: rxminder
component: database
name: couchdb-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard
- apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"autoscaling/v2","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend-hpa","namespace":"rxminder-dev"},"spec":{"maxReplicas":3,"metrics":[{"resource":{"name":"cpu","target":{"averageUtilization":50,"type":"Utilization"}},"type":"Resource"}],"minReplicas":1,"scaleTargetRef":{"apiVersion":"apps/v1","kind":"Deployment","name":"rxminder-frontend"}}}
labels:
app: rxminder
component: frontend
environment: dev
version: v1.0.0
name: rxminder-frontend-hpa
namespace: rxminder-dev
spec:
maxReplicas: 3
metrics:
- resource:
name: cpu
target:
averageUtilization: 50
type: Utilization
type: Resource
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: rxminder-frontend
- apiVersion: batch/v1
kind: Job
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-db-seed","namespace":"rxminder-dev"},"spec":{"backoffLimit":4,"template":{"metadata":{"labels":{"app":"rxminder","component":"database"}},"spec":{"containers":[{"args":["# Wait for CouchDB to be ready\necho \"Waiting for CouchDB to be ready...\"\nuntil curl -f http://couchdb-service:5984/_up 2\u003e/dev/null; do\n sleep 2\ndone\n\n# Create databases\necho \"Creating databases...\"\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app\n\n# Create default admin user\necho \"Creating default admin user...\"\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/_users/org.couchdb.user:$COUCHDB_USER \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"name\\\": \\\"$COUCHDB_USER\\\",\n \\\"password\\\": \\\"$COUCHDB_PASSWORD\\\",\n \\\"roles\\\": [\\\"admin\\\"],\n \\\"type\\\": \\\"user\\\"\n }\"\n\n# Create design documents for views\necho \"Creating design documents...\"\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/medications \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"views\": {\n \"by_name\": {\n \"map\": \"function(doc) { if (doc.type === \\\"medication\\\") emit(doc.name, doc); }\"\n },\n \"by_user\": {\n \"map\": \"function(doc) { if (doc.type === \\\"medication\\\") emit(doc.userId, doc); }\"\n }\n }\n }'\n\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/reminders \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"views\": {\n \"by_medication\": {\n \"map\": \"function(doc) { if (doc.type === \\\"reminder\\\") emit(doc.medicationId, doc); }\"\n },\n \"by_user\": {\n \"map\": \"function(doc) { if (doc.type === \\\"reminder\\\") emit(doc.userId, doc); }\"\n }\n }\n }'\n\n# Create a sample user document for reference\n # Create design document for authentication users\n curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/auth \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"views\": {\n \"by_username\": {\n \"map\": \"function(doc) { if (doc.type === \\\"user\\\" \u0026\u0026 doc.username) emit(doc.username, doc); }\"\n },\n \"by_email\": {\n \"map\": \"function(doc) { if (doc.type === \\\"user\\\" \u0026\u0026 doc.email) emit(doc.email, doc); }\"\n }\n }\n }'\necho \"Creating sample user document...\"\ncurl -X POST http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"type\": \"user\",\n \"name\": \"sample_user\",\n \"email\": \"user@example.com\",\n \"createdAt\": \"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'\"\n }'\n\necho \"Database seeding completed with default admin user\"\n"],"command":["/bin/sh","-c"],"env":[{"name":"COUCHDB_USER","valueFrom":{"secretKeyRef":{"key":"username","name":"couchdb-secret-7ck2cc96g5"}}},{"name":"COUCHDB_PASSWORD","valueFrom":{"secretKeyRef":{"key":"password","name":"couchdb-secret-7ck2cc96g5"}}}],"image":"couchdb:3.3.2","name":"db-seeder"}],"restartPolicy":"Never"}}}}
labels:
app: rxminder
component: database
environment: dev
version: v1.0.0
name: rxminder-db-seed
namespace: rxminder-dev
spec:
backoffLimit: 4
template:
metadata:
labels:
app: rxminder
component: database
spec:
containers:
- args:
- |
# Wait for CouchDB to be ready
echo "Waiting for CouchDB to be ready..."
until curl -f http://couchdb-service:5984/_up 2>/dev/null; do
sleep 2
done
# Create databases
echo "Creating databases..."
curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app
# Create default admin user
echo "Creating default admin user..."
curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/_users/org.couchdb.user:$COUCHDB_USER \
-H "Content-Type: application/json" \
-d "{
\"name\": \"$COUCHDB_USER\",
\"password\": \"$COUCHDB_PASSWORD\",
\"roles\": [\"admin\"],
\"type\": \"user\"
}"
# Create design documents for views
echo "Creating design documents..."
curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/medications \
-H "Content-Type: application/json" \
-d '{
"views": {
"by_name": {
"map": "function(doc) { if (doc.type === \"medication\") emit(doc.name, doc); }"
},
"by_user": {
"map": "function(doc) { if (doc.type === \"medication\") emit(doc.userId, doc); }"
}
}
}'
curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/reminders \
-H "Content-Type: application/json" \
-d '{
"views": {
"by_medication": {
"map": "function(doc) { if (doc.type === \"reminder\") emit(doc.medicationId, doc); }"
},
"by_user": {
"map": "function(doc) { if (doc.type === \"reminder\") emit(doc.userId, doc); }"
}
}
}'
# Create a sample user document for reference
# Create design document for authentication users
curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/auth \
-H "Content-Type: application/json" \
-d '{
"views": {
"by_username": {
"map": "function(doc) { if (doc.type === \"user\" && doc.username) emit(doc.username, doc); }"
},
"by_email": {
"map": "function(doc) { if (doc.type === \"user\" && doc.email) emit(doc.email, doc); }"
}
}
}'
echo "Creating sample user document..."
curl -X POST http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app \
-H "Content-Type: application/json" \
-d '{
"type": "user",
"name": "sample_user",
"email": "user@example.com",
"createdAt": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}'
echo "Database seeding completed with default admin user"
command:
- /bin/sh
- -c
env:
- name: COUCHDB_USER
valueFrom:
secretKeyRef:
key: username
name: couchdb-secret-7ck2cc96g5
- name: COUCHDB_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: couchdb-secret-7ck2cc96g5
image: couchdb:3.3.2
name: db-seeder
restartPolicy: Never
- apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-ingress","namespace":"rxminder-dev"},"spec":{"ingressClassName":"nginx","rules":[{"host":"rxminder-dev.local","http":{"paths":[{"backend":{"service":{"name":"rxminder-frontend-service","port":{"number":80}}},"path":"/","pathType":"Prefix"}]}}]}}
labels:
app: rxminder
component: frontend
environment: dev
version: v1.0.0
name: rxminder-ingress
namespace: rxminder-dev
spec:
ingressClassName: nginx
rules:
- host: rxminder-dev.local
http:
paths:
- backend:
service:
name: rxminder-frontend-service
port:
number: 80
path: /
pathType: Prefix
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"networking.k8s.io/v1","kind":"NetworkPolicy","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-database-policy","namespace":"rxminder-dev"},"spec":{"egress":[{"ports":[{"port":5984,"protocol":"TCP"}],"to":[{"podSelector":{"matchLabels":{"component":"database"}}}]}],"ingress":[{"from":[{"podSelector":{"matchLabels":{"component":"frontend"}}}],"ports":[{"port":5984,"protocol":"TCP"}]}],"podSelector":{"matchLabels":{"component":"database"}},"policyTypes":["Ingress","Egress"]}}
labels:
app: rxminder
component: database
environment: dev
version: v1.0.0
name: rxminder-database-policy
namespace: rxminder-dev
spec:
egress:
- ports:
- port: 5984
protocol: TCP
to:
- podSelector:
matchLabels:
component: database
ingress:
- from:
- podSelector:
matchLabels:
component: frontend
ports:
- port: 5984
protocol: TCP
podSelector:
matchLabels:
component: database
policyTypes:
- Ingress
- Egress
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"networking.k8s.io/v1","kind":"NetworkPolicy","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend-policy","namespace":"rxminder-dev"},"spec":{"egress":[{"ports":[{"port":5984,"protocol":"TCP"}],"to":[{"podSelector":{"matchLabels":{"component":"database"}}}]},{"ports":[{"port":80,"protocol":"TCP"}],"to":[{"podSelector":{"matchLabels":{"component":"frontend"}}}]}],"ingress":[{"from":[{"podSelector":{"matchLabels":{"component":"frontend"}}}],"ports":[{"port":80,"protocol":"TCP"}]}],"podSelector":{"matchLabels":{"component":"frontend"}},"policyTypes":["Ingress","Egress"]}}
labels:
app: rxminder
component: frontend
environment: dev
version: v1.0.0
name: rxminder-frontend-policy
namespace: rxminder-dev
spec:
egress:
- ports:
- port: 5984
protocol: TCP
to:
- podSelector:
matchLabels:
component: database
- ports:
- port: 80
protocol: TCP
to:
- podSelector:
matchLabels:
component: frontend
ingress:
- from:
- podSelector:
matchLabels:
component: frontend
ports:
- port: 80
protocol: TCP
podSelector:
matchLabels:
component: frontend
policyTypes:
- Ingress
- Egress
kind: List
metadata: {}
## Migration Path
- Legacy shell scripts remain available for backward compatibility
- Gradual migration: dev → staging → production
- Zero-downtime deployment capability
Co-authored-by: Assistant <assistant@anthropic.com>