Merge branch 'release/3.0.1-beta'

This commit is contained in:
Josako
2025-08-21 15:25:07 +02:00
39 changed files with 6208 additions and 494 deletions

32
check_running_services.sh Normal file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
# Diagnostic script to check what services are running
echo "=== KIND CLUSTER STATUS ==="
echo "Namespaces:"
kubectl get namespaces | grep eveai
echo -e "\nPods in eveai-dev:"
kubectl get pods -n eveai-dev
echo -e "\nServices in eveai-dev:"
kubectl get services -n eveai-dev
echo -e "\n=== TEST CONTAINERS STATUS ==="
echo "Running test containers:"
podman ps | grep eveai_test
echo -e "\n=== PORT ANALYSIS ==="
echo "What's listening on port 3080:"
lsof -i :3080 2>/dev/null || echo "Nothing found"
echo -e "\nWhat's listening on port 4080:"
lsof -i :4080 2>/dev/null || echo "Nothing found"
echo -e "\n=== SOLUTION ==="
echo "The application you see is from TEST CONTAINERS (6 days old),"
echo "NOT from the Kind cluster (3 minutes old)."
echo ""
echo "To test Kind cluster:"
echo "1. Stop test containers: podman stop eveai_test_nginx_1 eveai_test_eveai_app_1"
echo "2. Deploy Kind services: kup-all-structured"
echo "3. Restart test containers if needed"

View File

@@ -23,7 +23,7 @@ task_description: >
Create a prioritised list of the 10 most critical competencies as defined above, ranked in importance. Create a prioritised list of the 10 most critical competencies as defined above, ranked in importance.
Treat this as a logical and professional reasoning exercise. Treat this as a logical and professional reasoning exercise.
Respect the language of the vacancy text, and return answers / output in the same language. Respect the language of the vacancy text, and return answers / output in the same language. Only use plain text.
{custom_description} {custom_description}

View File

@@ -5,6 +5,23 @@ All notable changes to EveAI will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.0.1-beta]
### Changed
- Podman now replaces Docker for building images
- Local registry now replaces Docker Hub
- Start for k8s integration
- RAG possibilities throughout usage of the TRAICIE_SELECTION_SPECIALIST
### Fixed
- TRA-67 - initial edit of Tenant Make --> 2-step proces
- TRA-68 - Correction of javascript for json editor, resulting in Asset Changes not being saved
- TRA-70 - Wrong File Size display for Assets
- TRA-69 - Wrong number of questions in TRAICIE_KO_INTERVIEW_DEFINITION_SPECIALIST (required correction in TRACIE_ROLE_DEFINITION_SPECIALIST)
### Security
- In case of vulnerabilities.
## [3.0.0-beta] ## [3.0.0-beta]
### Added ### Added

View File

@@ -3,19 +3,29 @@
# Exit on any error # Exit on any error
set -e set -e
source ./docker_env_switch.sh dev source ./podman_env_switch.sh dev
# Load environment variables # Load environment variables
source .env source .env
# Docker registry # Check if podman is available
REGISTRY="josakola" if ! command -v podman &> /dev/null; then
echo "Error: podman not found"
exit 1
fi
echo "Using container runtime: podman"
# Local registry
REGISTRY="registry.ask-eve-ai-local.com"
# Account prefix voor consistency met Docker Hub
ACCOUNT="josakola"
# Tag (you might want to use a version or git commit hash) # Tag (you might want to use a version or git commit hash)
TAG="latest" TAG="latest"
# Platforms to build for # Single platform - AMD64 only for simplicity
PLATFORMS="linux/amd64,linux/arm64" PLATFORM="linux/amd64"
# Default action # Default action
ACTION="both" ACTION="both"
@@ -28,13 +38,14 @@ DEBUG=""
# Function to display usage information # Function to display usage information
usage() { usage() {
echo "Usage: $0 [-b|-p] [--no-cache] [--progress=plain] [--debug] [service1 service2 ...]" echo "Usage: $0 [-b|-p] [--no-cache] [--progress=plain] [--debug] [service1 service2 ...]"
echo " -b: Build only (for current platform)" echo " -b: Build only"
echo " -p: Push only (multi-platform)" echo " -p: Push only"
echo " --no-cache: Perform a clean build without using cache" echo " --no-cache: Perform a clean build without using cache"
echo " --progress=plain: Show detailed progress of the build" echo " --progress=plain: Show detailed progress of the build"
echo " --debug: Enable debug mode for the build" echo " --debug: Enable debug mode for the build"
echo " If no option is provided, both build and push will be performed." echo " If no option is provided, both build and push will be performed."
echo " If no services are specified, all eveai_ services and nginx will be processed." echo " If no services are specified, all eveai_ services and nginx will be processed."
echo " All images are built for AMD64 platform (compatible with both x86_64 and Apple Silicon via emulation)."
} }
# Parse command-line options # Parse command-line options
@@ -92,47 +103,57 @@ process_service() {
return 1 return 1
fi fi
# Construct image names
LOCAL_IMAGE_NAME="$ACCOUNT/$SERVICE:$TAG"
REGISTRY_IMAGE_NAME="$REGISTRY/$ACCOUNT/$SERVICE:$TAG"
echo "Building for platform: $PLATFORM"
echo "Local tag: $LOCAL_IMAGE_NAME"
echo "Registry tag: $REGISTRY_IMAGE_NAME"
# Build and/or push based on ACTION # Build and/or push based on ACTION
if [ "$ACTION" = "build" ]; then if [ "$ACTION" = "build" ]; then
echo "Building $SERVICE for current platform..." echo "Building $SERVICE for $PLATFORM..."
docker build \ podman build \
--platform "$PLATFORM" \
$NO_CACHE \ $NO_CACHE \
$PROGRESS \ $PROGRESS \
$DEBUG \ $DEBUG \
-t "$REGISTRY/$SERVICE:$TAG" \ -t "$LOCAL_IMAGE_NAME" \
-f "$CONTEXT/$DOCKERFILE" \ -t "$REGISTRY_IMAGE_NAME" \
"$CONTEXT"
elif [ "$ACTION" = "push" ]; then
echo "Building and pushing $SERVICE for multiple platforms..."
docker buildx build \
$NO_CACHE \
$PROGRESS \
$DEBUG \
--platform "$PLATFORMS" \
-t "$REGISTRY/$SERVICE:$TAG" \
-f "$CONTEXT/$DOCKERFILE" \
"$CONTEXT" \
--push
else
echo "Building $SERVICE for current platform..."
docker build \
$NO_CACHE \
$PROGRESS \
$DEBUG \
-t "$REGISTRY/$SERVICE:$TAG" \
-f "$CONTEXT/$DOCKERFILE" \ -f "$CONTEXT/$DOCKERFILE" \
"$CONTEXT" "$CONTEXT"
echo "Building and pushing $SERVICE for multiple platforms..." elif [ "$ACTION" = "push" ]; then
docker buildx build \ echo "Building and pushing $SERVICE for $PLATFORM..."
podman build \
--platform "$PLATFORM" \
$NO_CACHE \ $NO_CACHE \
$PROGRESS \ $PROGRESS \
$DEBUG \ $DEBUG \
--platform "$PLATFORMS" \ -t "$LOCAL_IMAGE_NAME" \
-t "$REGISTRY/$SERVICE:$TAG" \ -t "$REGISTRY_IMAGE_NAME" \
-f "$CONTEXT/$DOCKERFILE" \ -f "$CONTEXT/$DOCKERFILE" \
"$CONTEXT" \ "$CONTEXT"
--push
echo "Pushing $SERVICE to registry..."
podman push "$REGISTRY_IMAGE_NAME"
else
# Both build and push
echo "Building $SERVICE for $PLATFORM..."
podman build \
--platform "$PLATFORM" \
$NO_CACHE \
$PROGRESS \
$DEBUG \
-t "$LOCAL_IMAGE_NAME" \
-t "$REGISTRY_IMAGE_NAME" \
-f "$CONTEXT/$DOCKERFILE" \
"$CONTEXT"
echo "Pushing $SERVICE to registry..."
podman push "$REGISTRY_IMAGE_NAME"
fi fi
} }
@@ -146,31 +167,25 @@ else
SERVICES=("$@") SERVICES=("$@")
fi fi
# Check if eveai_builder exists, if not create it echo "Using simplified AMD64-only approach for maximum compatibility..."
if ! docker buildx inspect eveai_builder > /dev/null 2>&1; then echo "Images will be tagged as: $REGISTRY/$ACCOUNT/[service]:$TAG"
echo "Creating eveai_builder..."
docker buildx create --name eveai_builder
fi
# Use eveai_builder
echo "Using eveai_builder..."
docker buildx use eveai_builder
# Loop through services # Loop through services
for SERVICE in "${SERVICES[@]}"; do for SERVICE in "${SERVICES[@]}"; do
if [[ "$SERVICE" == "nginx" ]]; then if [[ "$SERVICE" == "nginx" ]]; then
./copy_specialist_svgs.sh ../config ../nginx/static/assets ./copy_specialist_svgs.sh ../config ../nginx/static/assets 2>/dev/null || echo "Warning: copy_specialist_svgs.sh not found or failed"
fi fi
if [[ "$SERVICE" == "nginx" || "$SERVICE" == eveai_* || "$SERVICE" == "flower" || "$SERVICE" == "prometheus" || "$SERVICE" == "grafana" ]]; then if [[ "$SERVICE" == "nginx" || "$SERVICE" == eveai_* || "$SERVICE" == "flower" || "$SERVICE" == "prometheus" || "$SERVICE" == "grafana" ]]; then
if process_service "$SERVICE"; then if process_service "$SERVICE"; then
echo "Successfully processed $SERVICE" echo "Successfully processed $SERVICE"
else else
echo "Failed to process $SERVICE" echo "Failed to process $SERVICE"
fi fi
else else
echo "Skipping $SERVICE as it's not nginx, flower, prometheus, grafana or doesn't start with eveai_" echo "⏭️ Skipping $SERVICE as it's not nginx, flower, prometheus, grafana or doesn't start with eveai_"
fi fi
done done
echo -e "\033[35mAll specified services processed.\033[0m" echo -e "\033[32m✅ All specified services processed successfully!\033[0m"
echo -e "\033[35mFinished at $(date +"%d/%m/%Y %H:%M:%S")\033[0m" echo -e "\033[32m📦 Images are available locally and in registry\033[0m"
echo -e "\033[32m🕐 Finished at $(date +"%d/%m/%Y %H:%M:%S")\033[0m"

View File

@@ -1,13 +1,4 @@
# Comments are provided throughout this file to help you get started. # Podman Compose compatible versie met port schema compliance
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
x-common-variables: &common-variables x-common-variables: &common-variables
DB_HOST: db DB_HOST: db
DB_USER: luke DB_USER: luke
@@ -45,16 +36,12 @@ x-common-variables: &common-variables
services: services:
nginx: nginx:
image: josakola/nginx:latest image: ${REGISTRY_PREFIX:-}josakola/nginx:latest
build: build:
context: .. context: ..
dockerfile: ./docker/nginx/Dockerfile dockerfile: ./docker/nginx/Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 80:80 - 3080:80 # Dev nginx proxy volgens port schema
- 8080:8080
environment: environment:
<<: *common-variables <<: *common-variables
volumes: volumes:
@@ -72,18 +59,15 @@ services:
- eveai_api - eveai_api
- eveai_chat_client - eveai_chat_client
networks: networks:
- eveai-network - eveai-dev-network
eveai_app: eveai_app:
image: josakola/eveai_app:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_app:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_app/Dockerfile dockerfile: ./docker/eveai_app/Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 5001:5001 - 3001:5001 # Dev app volgens port schema
expose: expose:
- 8000 - 8000
environment: environment:
@@ -108,20 +92,17 @@ services:
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5001/healthz/ready"] test: ["CMD", "curl", "-f", "http://localhost:5001/healthz/ready"]
interval: 30s interval: 30s
timeout: 1s timeout: 10s
retries: 3 retries: 3
start_period: 30s start_period: 60s
networks: networks:
- eveai-network - eveai-dev-network
eveai_workers: eveai_workers:
image: josakola/eveai_workers:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_workers:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_workers/Dockerfile dockerfile: ./docker/eveai_workers/Dockerfile
platforms:
- linux/amd64
- linux/arm64
expose: expose:
- 8000 - 8000
environment: environment:
@@ -142,18 +123,15 @@ services:
minio: minio:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-dev-network
eveai_chat_client: eveai_chat_client:
image: josakola/eveai_chat_client:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_chat_client:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_chat_client/Dockerfile dockerfile: ./docker/eveai_chat_client/Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 5004:5004 - 3004:5004 # Dev chat client volgens port schema
expose: expose:
- 8000 - 8000
environment: environment:
@@ -176,20 +154,17 @@ services:
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5004/healthz/ready"] test: ["CMD", "curl", "-f", "http://localhost:5004/healthz/ready"]
interval: 30s interval: 30s
timeout: 1s timeout: 10s
retries: 3 retries: 3
start_period: 30s start_period: 60s
networks: networks:
- eveai-network - eveai-dev-network
eveai_chat_workers: eveai_chat_workers:
image: josakola/eveai_chat_workers:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_chat_workers:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_chat_workers/Dockerfile dockerfile: ./docker/eveai_chat_workers/Dockerfile
platforms:
- linux/amd64
- linux/arm64
expose: expose:
- 8000 - 8000
environment: environment:
@@ -208,26 +183,20 @@ services:
redis: redis:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-dev-network
eveai_api: eveai_api:
image: josakola/eveai_api:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_api:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_api/Dockerfile dockerfile: ./docker/eveai_api/Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 5003:5003 - 3003:5003 # Dev API volgens port schema
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_api COMPONENT_NAME: eveai_api
WORDPRESS_HOST: host.docker.internal
WORDPRESS_PORT: 10003
WORDPRESS_PROTOCOL: http
volumes: volumes:
- ../eveai_api:/app/eveai_api - ../eveai_api:/app/eveai_api
- ../common:/app/common - ../common:/app/common
@@ -245,20 +214,17 @@ services:
healthcheck: healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:5003/healthz/ready" ] test: [ "CMD", "curl", "-f", "http://localhost:5003/healthz/ready" ]
interval: 30s interval: 30s
timeout: 1s timeout: 10s
retries: 3 retries: 3
start_period: 30s start_period: 60s
networks: networks:
- eveai-network - eveai-dev-network
eveai_beat: eveai_beat:
image: josakola/eveai_beat:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_beat:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_beat/Dockerfile dockerfile: ./docker/eveai_beat/Dockerfile
platforms:
- linux/amd64
- linux/arm64
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_beat COMPONENT_NAME: eveai_beat
@@ -273,16 +239,13 @@ services:
redis: redis:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-dev-network
eveai_entitlements: eveai_entitlements:
image: josakola/eveai_entitlements:latest image: ${REGISTRY_PREFIX:-}josakola/eveai_entitlements:latest
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_entitlements/Dockerfile dockerfile: ./docker/eveai_entitlements/Dockerfile
platforms:
- linux/amd64
- linux/arm64
expose: expose:
- 8000 - 8000
environment: environment:
@@ -303,13 +266,13 @@ services:
minio: minio:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-dev-network
db: db:
hostname: db hostname: db
image: ankane/pgvector image: ankane/pgvector
ports: ports:
- 5432:5432 - 3005:5432 # Dev database volgens port schema (vermijd standaard 5432)
restart: always restart: always
environment: environment:
- POSTGRES_DB=eveai - POSTGRES_DB=eveai
@@ -324,13 +287,13 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
networks: networks:
- eveai-network - eveai-dev-network
redis: redis:
image: redis:7.2.5 image: redis:7.2.5
restart: always restart: always
ports: ports:
- "6379:6379" - "3006:6379" # Dev Redis volgens port schema (vermijd standaard 6379)
volumes: volumes:
- ./db/redis:/data - ./db/redis:/data
healthcheck: healthcheck:
@@ -339,10 +302,10 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
networks: networks:
- eveai-network - eveai-dev-network
flower: flower:
image: josakola/flower:latest image: ${REGISTRY_PREFIX:-}josakola/flower:latest
build: build:
context: .. context: ..
dockerfile: ./docker/flower/Dockerfile dockerfile: ./docker/flower/Dockerfile
@@ -351,17 +314,17 @@ services:
volumes: volumes:
- ../scripts:/app/scripts - ../scripts:/app/scripts
ports: ports:
- "5555:5555" - "3007:5555" # Dev Flower volgens port schema
depends_on: depends_on:
- redis - redis
networks: networks:
- eveai-network - eveai-dev-network
minio: minio:
image: minio/minio image: minio/minio
ports: ports:
- "9000:9000" - "3008:9000" # Dev MinIO volgens port schema
- "9001:9001" - "3009:9001" # Dev MinIO console
expose: expose:
- 9000 - 9000
volumes: volumes:
@@ -376,18 +339,17 @@ services:
interval: 30s interval: 30s
timeout: 20s timeout: 20s
retries: 3 retries: 3
start_period: 30s start_period: 60s
networks: networks:
- eveai-network - eveai-dev-network
prometheus: prometheus:
image: prom/prometheus:latest image: ${REGISTRY_PREFIX:-}josakola/prometheus:latest
build: build:
context: ./prometheus context: ./prometheus
dockerfile: Dockerfile dockerfile: Dockerfile
container_name: prometheus
ports: ports:
- "9090:9090" - "3010:9090" # Dev Prometheus volgens port schema
volumes: volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/data:/prometheus - ./prometheus/data:/prometheus
@@ -399,24 +361,23 @@ services:
- '--web.enable-lifecycle' - '--web.enable-lifecycle'
restart: unless-stopped restart: unless-stopped
networks: networks:
- eveai-network - eveai-dev-network
pushgateway: pushgateway:
image: prom/pushgateway:latest image: prom/pushgateway:latest
restart: unless-stopped restart: unless-stopped
ports: ports:
- "9091:9091" - "3011:9091" # Dev Pushgateway volgens port schema
networks: networks:
- eveai-network - eveai-dev-network
grafana: grafana:
image: grafana/grafana:latest image: ${REGISTRY_PREFIX:-}josakola/grafana:latest
build: build:
context: ./grafana context: ./grafana
dockerfile: Dockerfile dockerfile: Dockerfile
container_name: grafana
ports: ports:
- "3000:3000" - "3012:3000" # Dev Grafana volgens port schema
volumes: volumes:
- ./grafana/provisioning:/etc/grafana/provisioning - ./grafana/provisioning:/etc/grafana/provisioning
- ./grafana/data:/var/lib/grafana - ./grafana/data:/var/lib/grafana
@@ -428,21 +389,12 @@ services:
depends_on: depends_on:
- prometheus - prometheus
networks: networks:
- eveai-network - eveai-dev-network
networks: networks:
eveai-network: eveai-dev-network:
driver: bridge driver: bridge
# This enables the containers to access the host network
driver_opts:
com.docker.network.bridge.host_ipc: "true"
volumes: volumes:
minio_data: minio_data:
eveai_logs: eveai_logs:
# db-data:
# redis-data:
# tenant-files:
#secrets:
# db-password:
# file: ./db/password.txt

View File

@@ -12,7 +12,7 @@ x-common-variables: &common-variables
DB_HOST: minty.ask-eve-ai-local.com DB_HOST: minty.ask-eve-ai-local.com
DB_USER: luke DB_USER: luke
DB_PASS: 'Skywalker!' DB_PASS: 'Skywalker!'
DB_NAME: eveai DB_NAME: eveai_test
DB_PORT: '5432' DB_PORT: '5432'
FLASK_ENV: test FLASK_ENV: test
FLASK_DEBUG: true FLASK_DEBUG: true
@@ -43,36 +43,36 @@ x-common-variables: &common-variables
SW_EMAIL_NAME: "Evie Admin (test)" SW_EMAIL_NAME: "Evie Admin (test)"
SW_PROJECT: "f282f55a-ea52-4538-a979-5bcb890717ab" SW_PROJECT: "f282f55a-ea52-4538-a979-5bcb890717ab"
name: eveai_test
services: services:
nginx: nginx:
image: josakola/nginx:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/nginx:latest
ports: ports:
- 80:80 - 4080:80
- 8080:8080
environment: environment:
<<: *common-variables <<: *common-variables
volumes: volumes:
- eveai_logs:/var/log/nginx - test_eveai_logs:/var/log/nginx
depends_on: depends_on:
- eveai_app - eveai_app
- eveai_api - eveai_api
- eveai_chat_client - eveai_chat_client
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_app: eveai_app:
image: josakola/eveai_app:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_app:latest
ports: ports:
- 5001:5001 - 4001:5001
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_app COMPONENT_NAME: eveai_app
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
@@ -85,40 +85,38 @@ services:
retries: 3 retries: 3
start_period: 30s start_period: 30s
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_workers: eveai_workers:
image: josakola/eveai_workers:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_workers:latest
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_workers COMPONENT_NAME: eveai_workers
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
minio: minio:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_chat_client: eveai_chat_client:
image: josakola/eveai_chat_client:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_chat_client:latest
ports: ports:
- 5004:5004 - 4004:5004
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_chat_client COMPONENT_NAME: eveai_chat_client
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
@@ -131,38 +129,36 @@ services:
retries: 3 retries: 3
start_period: 30s start_period: 30s
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_chat_workers: eveai_chat_workers:
image: josakola/eveai_chat_workers:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_chat_workers:latest
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_chat_workers COMPONENT_NAME: eveai_chat_workers
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_api: eveai_api:
image: josakola/eveai_api:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_api:latest
ports: ports:
- 5003:5003 - 4003:5003
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_api COMPONENT_NAME: eveai_api
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
@@ -175,80 +171,78 @@ services:
retries: 3 retries: 3
start_period: 30s start_period: 30s
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_beat: eveai_beat:
image: josakola/eveai_beat:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_beat:latest
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_beat COMPONENT_NAME: eveai_beat
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
eveai_entitlements: eveai_entitlements:
image: josakola/eveai_entitlements:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/eveai_entitlements:latest
expose: expose:
- 8000 - 8000
environment: environment:
<<: *common-variables <<: *common-variables
COMPONENT_NAME: eveai_entitlements COMPONENT_NAME: eveai_entitlements
volumes: volumes:
- eveai_logs:/app/logs - test_eveai_logs:/app/logs
- crewai_storage:/app/crewai_storage
depends_on: depends_on:
redis: redis:
condition: service_healthy condition: service_healthy
minio: minio:
condition: service_healthy condition: service_healthy
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
redis: redis:
image: redis:7.2.5 image: redis:7.2.5
restart: no restart: unless-stopped
ports: ports:
- "6379:6379" - "4006:6379"
volumes: volumes:
- redisdata:/data - test_redisdata:/data
healthcheck: healthcheck:
test: [ "CMD", "redis-cli", "ping" ] test: [ "CMD", "redis-cli", "ping" ]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
networks: networks:
- eveai-network - eveai-test-network
flower: flower:
image: josakola/flower:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/flower:latest
environment: environment:
<<: *common-variables <<: *common-variables
ports: ports:
- "5555:5555" - "4007:5555"
depends_on: depends_on:
- redis - redis
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
minio: minio:
image: minio/minio image: minio/minio
ports: ports:
- "9000:9000" - "4008:9000"
- "9001:9001" - "4009:9001"
expose: expose:
- 9000 - 9000
volumes: volumes:
- miniodata:/data - test_miniodata:/data
- minioconfig:/root/.minio - test_minioconfig:/root/.minio
environment: environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
@@ -260,64 +254,57 @@ services:
retries: 3 retries: 3
start_period: 30s start_period: 30s
networks: networks:
- eveai-network - eveai-test-network
restart: "no" restart: unless-stopped
prometheus: prometheus:
image: josakola/prometheus:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/prometheus:${EVEAI_VERSION:-latest}
container_name: prometheus
ports: ports:
- "9090:9090" - "4010:9090"
volumes: volumes:
- prometheusdata:/prometheus - test_prometheusdata:/prometheus
command: command:
- '--config.file=/etc/prometheus/prometheus.yml' - '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus' - '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries' - '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles' - '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle' - '--web.enable-lifecycle'
restart: no restart: unless-stopped
networks: networks:
- eveai-network - eveai-test-network
pushgateway: pushgateway:
image: prom/pushgateway:latest image: prom/pushgateway:latest
restart: unless-stopped restart: unless-stopped
ports: ports:
- "9091:9091" - "4011:9091"
networks: networks:
- eveai-network - eveai-test-network
grafana: grafana:
image: josakola/grafana:${EVEAI_VERSION:-latest} image: ${REGISTRY_PREFIX:-}josakola/grafana:${EVEAI_VERSION:-latest}
container_name: grafana
ports: ports:
- "3000:3000" - "4012:3000"
volumes: volumes:
- grafanadata:/var/lib/grafana - test_grafanadata:/var/lib/grafana
environment: environment:
- GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin - GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false - GF_USERS_ALLOW_SIGN_UP=false
restart: no restart: unless-stopped
depends_on: depends_on:
- prometheus - prometheus
networks: networks:
- eveai-network - eveai-test-network
networks: networks:
eveai-network: eveai-test-network:
driver: bridge driver: bridge
# This enables the containers to access the host network
driver_opts:
com.docker.network.bridge.host_ipc: "true"
volumes: volumes:
eveai_logs: test_eveai_logs:
pgdata: test_redisdata:
redisdata: test_miniodata:
miniodata: test_minioconfig:
minioconfig: test_prometheusdata:
prometheusdata: test_grafanadata:
grafanadata:
crewai_storage:

View File

@@ -1,155 +0,0 @@
#!/bin/zsh
# or use #!/usr/bin/env zsh
# Function to display usage information
usage() {
echo "Usage: source $0 <environment> [version]"
echo " environment: The environment to use (dev, prod, test, integration, bugfix)"
echo " version : (Optional) Specific release version to deploy"
echo " If not specified, uses 'latest' (except for dev environment)"
}
# Replace the existing check at the beginning of docker_env_switch.sh
# Check if the script is sourced
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Script is being executed directly from terminal
echo "Error: This script must be sourced, not executed directly."
echo "Please run: source $0 <environment> [version]"
exit 1
fi
# If we reach here, script is being sourced (either by terminal or another script)
# Check if an environment is provided
if [ $# -eq 0 ]; then
usage
return 1
fi
ENVIRONMENT=$1
VERSION=${2:-latest} # Default to latest if not specified
# Set variables based on the environment
case $ENVIRONMENT in
dev)
DOCKER_CONTEXT="default"
COMPOSE_FILE="compose_dev.yaml"
VERSION="latest" # Always use latest for dev
;;
prod)
DOCKER_CONTEXT="mxz536.stackhero-network.com"
COMPOSE_FILE="compose_stackhero.yaml"
;;
test)
DOCKER_CONTEXT="test-environment" # Change to your actual test Docker context
COMPOSE_FILE="compose_test.yaml"
;;
integration)
DOCKER_CONTEXT="integration-environment" # Change to your actual integration Docker context
COMPOSE_FILE="compose_integration.yaml"
;;
bugfix)
DOCKER_CONTEXT="bugfix-environment" # Change to your actual bugfix Docker context
COMPOSE_FILE="compose_bugfix.yaml"
;;
*)
echo "Invalid environment: $ENVIRONMENT"
usage
return 1
;;
esac
# Set Docker account
DOCKER_ACCOUNT="josakola"
# Check if Docker context exists
if ! docker context ls --format '{{.Name}}' | grep -q "^$DOCKER_CONTEXT$"; then
echo "Warning: Docker context '$DOCKER_CONTEXT' does not exist."
# Prompt user if they want to create the context
if [[ "$DOCKER_CONTEXT" != "default" ]]; then
echo "Do you want to set up this context now? (y/n): "
read CREATE_CONTEXT
if [[ "$CREATE_CONTEXT" == "y" || "$CREATE_CONTEXT" == "Y" ]]; then
# You would add here the specific code to create each context type
# For example, for remote contexts you might need SSH settings
echo "Please specify the Docker host URL (e.g., ssh://user@remote_host or tcp://remote_host:2375):"
read DOCKER_HOST
docker context create "$DOCKER_CONTEXT" --docker "host=$DOCKER_HOST"
if [ $? -ne 0 ]; then
echo "Failed to create Docker context. Please create it manually."
return 1
fi
else
echo "Using default context instead."
DOCKER_CONTEXT="default"
fi
fi
fi
# Check if compose file exists
if [ ! -f "$COMPOSE_FILE" ]; then
echo "Warning: Compose file '$COMPOSE_FILE' does not exist."
echo "Do you want to create it based on compose_dev.yaml? (y/n): "
read CREATE_FILE
if [[ "$CREATE_FILE" == "y" || "$CREATE_FILE" == "Y" ]]; then
# Create new compose file based on compose_dev.yaml with version variables
sed 's/\(image: josakola\/[^:]*\):latest/\1:${EVEAI_VERSION:-latest}/g' compose_dev.yaml > "$COMPOSE_FILE"
echo "Created $COMPOSE_FILE with version placeholders."
else
echo "Cannot proceed without a valid compose file."
return 1
fi
fi
# Switch Docker context
echo "Switching to Docker context: $DOCKER_CONTEXT"
docker context use $DOCKER_CONTEXT
# Set environment variables
export COMPOSE_FILE=$COMPOSE_FILE
export EVEAI_VERSION=$VERSION
export DOCKER_ACCOUNT=$DOCKER_ACCOUNT
echo "Set COMPOSE_FILE to $COMPOSE_FILE"
echo "Set EVEAI_VERSION to $VERSION"
echo "Set DOCKER_ACCOUNT to $DOCKER_ACCOUNT"
docker-compose() {
docker compose -f $COMPOSE_FILE "$@"
}
dc() {
docker compose -f $COMPOSE_FILE "$@"
}
dcup() {
docker compose -f $COMPOSE_FILE up -d --remove-orphans "$@"
}
dcdown() {
docker compose -f $COMPOSE_FILE down "$@"
}
dcps() {
docker compose -f $COMPOSE_FILE ps "$@"
}
dclogs() {
docker compose -f $COMPOSE_FILE logs "$@"
}
dcpull() {
docker compose -f $COMPOSE_FILE pull "$@"
}
dcrefresh() {
docker compose -f $COMPOSE_FILE pull && docker compose -f $COMPOSE_FILE up -d --remove-orphans "$@"
}
# Exporteer de functies zodat ze beschikbaar zijn in andere scripts
export -f docker-compose dc dcup dcdown dcps dclogs dcpull dcrefresh
echo "Docker environment switched to $ENVIRONMENT with version $VERSION"
echo "You can now use 'docker-compose', 'dc', 'dcup', 'dcdown', 'dcps', 'dclogs', 'dcpull' or 'dcrefresh' commands"

257
docker/podman_env_switch.sh Executable file
View File

@@ -0,0 +1,257 @@
#!/usr/bin/env zsh
# Function to display usage information
usage() {
echo "Usage: source $0 <environment> [version]"
echo " environment: The environment to use (dev, prod, test, integration, bugfix)"
echo " version : (Optional) Specific release version to deploy"
echo " If not specified, uses 'latest' (except for dev environment)"
}
# Check if the script is sourced - improved for both bash and zsh
is_sourced() {
if [[ -n "$ZSH_VERSION" ]]; then
# In zsh, check if we're in a sourced context
[[ "$ZSH_EVAL_CONTEXT" =~ "(:file|:cmdsubst)" ]] || [[ "$0" != "$ZSH_ARGZERO" ]]
else
# In bash, compare BASH_SOURCE with $0
[[ "${BASH_SOURCE[0]}" != "${0}" ]]
fi
}
if ! is_sourced; then
echo "Error: This script must be sourced, not executed directly."
echo "Please run: source $0 <environment> [version]"
if [[ -n "$ZSH_VERSION" ]]; then
return 1 2>/dev/null || exit 1
else
exit 1
fi
fi
# Check if an environment is provided
if [ $# -eq 0 ]; then
usage
return 1
fi
ENVIRONMENT=$1
VERSION=${2:-latest} # Default to latest if not specified
# Check if podman and podman-compose are available
if ! command -v podman &> /dev/null; then
echo "Error: podman is not installed or not in PATH"
echo "Please install podman first"
return 1
fi
if ! command -v podman-compose &> /dev/null; then
echo "Error: podman-compose is not installed or not in PATH"
echo "Please install podman-compose first"
return 1
fi
CONTAINER_CMD="podman"
# Store the actual path to podman-compose to avoid recursion
COMPOSE_CMD_PATH=$(command -v podman-compose)
echo "Using container runtime: $CONTAINER_CMD"
echo "Using compose command: $COMPOSE_CMD_PATH"
# Set default platform to AMD64 for consistency
export BUILDAH_PLATFORM=linux/amd64
export PODMAN_PLATFORM=linux/amd64
# Set variables based on the environment
case $ENVIRONMENT in
dev)
PODMAN_CONNECTION="default"
COMPOSE_FILE="compose_dev.yaml"
REGISTRY_PREFIX=""
COMPOSE_PROJECT_NAME="eveai_dev"
VERSION="latest" # Always use latest for dev
;;
prod)
# TO BE DEFINED
PODMAN_CONNECTION="mxz536.stackhero-network.com"
COMPOSE_FILE="compose_stackhero.yaml"
REGISTRY_PREFIX=""
COMPOSE_PROJECT_NAME="eveai_prod"
;;
test)
PODMAN_CONNECTION="test-environment"
COMPOSE_FILE="compose_test.yaml"
REGISTRY_PREFIX="registry.ask-eve-ai-local.com/"
COMPOSE_PROJECT_NAME="eveai_test"
;;
bugfix)
# TO BE DEFINED
PODMAN_CONNECTION="bugfix-environment"
COMPOSE_FILE="compose_bugfix.yaml"
COMPOSE_PROJECT_NAME="eveai_bugfix"
;;
*)
echo "Invalid environment: $ENVIRONMENT"
usage
return 1
;;
esac
# Set container registry account
CONTAINER_ACCOUNT="josakola"
# Handle remote connections for podman
if [[ "$PODMAN_CONNECTION" != "default" ]]; then
echo "Setting up remote podman connection: $PODMAN_CONNECTION"
# Check if podman connection exists
if ! podman system connection list --format '{{.Name}}' 2>/dev/null | grep -q "^$PODMAN_CONNECTION$"; then
echo "Warning: Podman connection '$PODMAN_CONNECTION' does not exist."
echo -n "Do you want to set up this connection now? (y/n): "
read -r CREATE_CONNECTION
if [[ "$CREATE_CONNECTION" == "y" || "$CREATE_CONNECTION" == "Y" ]]; then
echo -n "Please specify the SSH connection string (e.g., user@remote_host): "
read -r SSH_CONNECTION
if [[ -n "$SSH_CONNECTION" ]]; then
podman system connection add "$PODMAN_CONNECTION" --identity ~/.ssh/id_rsa "ssh://$SSH_CONNECTION/run/user/1000/podman/podman.sock"
if [[ $? -ne 0 ]]; then
echo "Failed to create podman connection. Please create it manually."
return 1
fi
else
echo "No SSH connection string provided."
return 1
fi
else
echo "Using local podman setup instead."
PODMAN_CONNECTION="default"
fi
fi
# Set the connection
if [[ "$PODMAN_CONNECTION" != "default" ]]; then
# Use podman context instead of manually setting CONTAINER_HOST
podman system connection default "$PODMAN_CONNECTION" 2>/dev/null
if [[ $? -eq 0 ]]; then
echo "Switched to remote podman connection: $PODMAN_CONNECTION"
else
echo "Warning: Failed to switch to connection $PODMAN_CONNECTION, using local setup"
PODMAN_CONNECTION="default"
fi
fi
else
echo "Using local podman setup with AMD64 platform"
# Ensure we're using the default local connection
podman system connection default "" 2>/dev/null || true
fi
# Check if compose file exists
if [[ ! -f "$COMPOSE_FILE" ]]; then
echo "Warning: Compose file '$COMPOSE_FILE' does not exist."
if [[ -f "compose_dev.yaml" ]]; then
echo -n "Do you want to create it based on compose_dev.yaml? (y/n): "
read -r CREATE_FILE
if [[ "$CREATE_FILE" == "y" || "$CREATE_FILE" == "Y" ]]; then
# Create new compose file based on compose_dev.yaml with version variables
if sed 's/\(image: josakola\/[^:]*\):latest/\1:${EVEAI_VERSION:-latest}/g' compose_dev.yaml > "$COMPOSE_FILE" 2>/dev/null; then
echo "Created $COMPOSE_FILE with version placeholders."
else
echo "Failed to create $COMPOSE_FILE"
return 1
fi
else
echo "Cannot proceed without a valid compose file."
return 1
fi
else
echo "Cannot create $COMPOSE_FILE: compose_dev.yaml not found."
return 1
fi
fi
# Set environment variables
export COMPOSE_FILE=$COMPOSE_FILE
export EVEAI_VERSION=$VERSION
export CONTAINER_ACCOUNT=$CONTAINER_ACCOUNT
export CONTAINER_CMD=$CONTAINER_CMD
export COMPOSE_CMD_PATH=$COMPOSE_CMD_PATH
export REGISTRY_PREFIX=$REGISTRY_PREFIX
export COMPOSE_PROJECT_NAME=$COMPOSE_PROJECT_NAME
echo "Set COMPOSE_FILE to $COMPOSE_FILE"
echo "Set EVEAI_VERSION to $VERSION"
echo "Set CONTAINER_ACCOUNT to $CONTAINER_ACCOUNT"
echo "Set platform to AMD64 (linux/amd64)"
echo "Set registry prefix to $REGISTRY_PREFIX"
echo "Set project name to $COMPOSE_PROJECT_NAME"
# Define compose wrapper functions using the full path to avoid recursion
pc() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE "$@"
}
pcup() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE up -d --remove-orphans "$@"
}
pcdown() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE down "$@"
}
pcps() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE ps "$@"
}
pclogs() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE logs "$@"
}
pcpull() {
echo "Pulling AMD64 images..."
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE pull "$@"
}
pcrefresh() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE pull && $COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE up -d --remove-orphans "$@"
}
pcbuild() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE build "$@"
}
pcrestart() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE restart "$@"
}
pcstop() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE stop "$@"
}
pcstart() {
$COMPOSE_CMD_PATH -p ${COMPOSE_PROJECT_NAME} -f $COMPOSE_FILE start "$@"
}
# Export functions - handle both bash and zsh
if [[ -n "$ZSH_VERSION" ]]; then
# In zsh, functions are automatically available in subshells
# But we can make them available globally with typeset
typeset -f pc pcup pcdown pcps pclogs pcpull pcrefresh pcbuild pcrestart pcstop pcstart > /dev/null
else
# Bash style export
export -f pc pcup pcdown pcps pclogs pcpull pcrefresh pcbuild pcrestart pcstop pcstart
fi
echo "✅ Podman environment switched to $ENVIRONMENT with version $VERSION"
echo "🖥️ Platform: AMD64 (compatible with both Intel and Apple Silicon)"
echo "Available commands:"
echo " pc - podman-compose shorthand"
echo " pcup - start services in background"
echo " pcdown - stop and remove services"
echo " pcps - list running services"
echo " pclogs - view service logs"
echo " pcpull - pull latest images"
echo " pcrefresh - pull and restart services"
echo " pcbuild - build services"
echo " pcrestart - restart services"
echo " pcstop - stop services"
echo " pcstart - start stopped services"

View File

@@ -0,0 +1,365 @@
# Containerd CRI Plugin Troubleshooting Guide
**Datum:** 18 augustus 2025
**Auteur:** EveAI Development Team
**Versie:** 1.0
## Overzicht
Dit document beschrijft de oplossing voor een kritiek probleem met de containerd Container Runtime Interface (CRI) plugin in het EveAI Kubernetes development cluster. Het probleem verhinderde de succesvolle opstart van Kind clusters en resulteerde in niet-functionele Kubernetes nodes.
## Probleem Beschrijving
### Symptomen
Het EveAI development cluster ondervond de volgende problemen:
1. **Kind cluster creatie faalde** met complexe kubeadmConfigPatches
2. **Control-plane nodes bleven in `NotReady` status**
3. **Container runtime toonde `Unknown` status**
4. **Kubelet kon niet communiceren** met de container runtime
5. **Ingress pods konden niet worden gescheduled**
6. **Cluster was volledig niet-functioneel**
### Foutmeldingen
#### Primaire Fout - Containerd CRI Plugin
```
failed to create CRI service: failed to create cni conf monitor for default:
failed to create fsnotify watcher: too many open files
```
#### Kubelet Communicatie Fouten
```
rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService
```
#### Node Status Problemen
```
NAME STATUS ROLES AGE VERSION
eveai-dev-cluster-control-plane NotReady control-plane 5m v1.33.1
```
## Root Cause Analyse
### Hoofdoorzaak
Het probleem had twee hoofdcomponenten:
1. **Complexe Kind Configuratie**: De oorspronkelijke `kind-dev-cluster.yaml` bevatte complexe `kubeadmConfigPatches` en `containerdConfigPatches` die de cluster initialisatie verstoorden.
2. **File Descriptor Limits**: De containerd service kon geen fsnotify watcher aanmaken voor CNI configuratie monitoring vanwege "too many open files" beperkingen binnen de Kind container omgeving.
### Technische Details
#### Kind Configuratie Problemen
De oorspronkelijke configuratie bevatte:
```yaml
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
etcd:
local:
dataDir: /tmp/lib/etcd
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
authorization-mode: "Webhook"
feature-gates: "EphemeralContainers=true"
```
#### Containerd CRI Plugin Failure
De containerd service startte wel op, maar de CRI plugin faalde tijdens het laden:
- **Service Status**: `active (running)`
- **CRI Plugin**: `failed to load`
- **Gevolg**: Kubelet kon niet communiceren met container runtime
## Oplossing Implementatie
### Stap 1: Kind Configuratie Vereenvoudiging
**Probleem**: Complexe kubeadmConfigPatches veroorzaakten initialisatie problemen.
**Oplossing**: Vereenvoudigde configuratie naar minimale, werkende setup:
```yaml
# Voor: Complexe configuratie
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
etcd:
local:
dataDir: /tmp/lib/etcd
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
authorization-mode: "Webhook"
feature-gates: "EphemeralContainers=true"
# Na: Vereenvoudigde configuratie
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
```
### Stap 2: Containerd ConfigPatches Uitschakeling
**Probleem**: Registry configuratie patches veroorzaakten containerd opstartproblemen.
**Oplossing**: Tijdelijk uitgeschakeld voor stabiliteit:
```yaml
# Temporarily disabled for testing
# containerdConfigPatches:
# - |-
# [plugins."io.containerd.grpc.v1.cri".registry]
# config_path = "/etc/containerd/certs.d"
```
### Stap 3: Setup Script Verbeteringen
#### A. Container Limits Configuratie Functie
Toegevoegd aan `setup-dev-cluster.sh`:
```bash
# Configure container resource limits to prevent CRI issues
configure_container_limits() {
print_status "Configuring container resource limits..."
# Configure file descriptor and inotify limits to prevent CRI plugin failures
podman exec "${CLUSTER_NAME}-control-plane" sh -c '
echo "fs.inotify.max_user_instances = 1024" >> /etc/sysctl.conf
echo "fs.inotify.max_user_watches = 524288" >> /etc/sysctl.conf
echo "fs.file-max = 2097152" >> /etc/sysctl.conf
sysctl -p
'
# Restart containerd to apply new limits
print_status "Restarting containerd with new limits..."
podman exec "${CLUSTER_NAME}-control-plane" systemctl restart containerd
# Wait for containerd to stabilize
sleep 10
# Restart kubelet to ensure proper CRI communication
podman exec "${CLUSTER_NAME}-control-plane" systemctl restart kubelet
print_success "Container limits configured and services restarted"
}
```
#### B. CRI Status Verificatie Functie
```bash
# Verify CRI status and functionality
verify_cri_status() {
print_status "Verifying CRI status..."
# Wait for services to stabilize
sleep 15
# Test CRI connectivity
if podman exec "${CLUSTER_NAME}-control-plane" crictl version &>/dev/null; then
print_success "CRI is functional"
# Show CRI version info
print_status "CRI version information:"
podman exec "${CLUSTER_NAME}-control-plane" crictl version
else
print_error "CRI is not responding - checking containerd logs"
podman exec "${CLUSTER_NAME}-control-plane" journalctl -u containerd --no-pager -n 20
print_error "Checking kubelet logs"
podman exec "${CLUSTER_NAME}-control-plane" journalctl -u kubelet --no-pager -n 10
return 1
fi
# Verify node readiness
print_status "Waiting for node to become Ready..."
local max_attempts=30
local attempt=0
while [ $attempt -lt $max_attempts ]; do
if kubectl get nodes | grep -q "Ready"; then
print_success "Node is Ready"
return 0
fi
attempt=$((attempt + 1))
print_status "Attempt $attempt/$max_attempts - waiting for node readiness..."
sleep 10
done
print_error "Node failed to become Ready within timeout"
kubectl get nodes -o wide
return 1
}
```
#### C. Hoofduitvoering Update
```bash
# Main execution
main() {
# ... existing code ...
check_prerequisites
create_host_directories
create_cluster
configure_container_limits # ← Nieuw toegevoegd
verify_cri_status # ← Nieuw toegevoegd
install_ingress_controller
apply_manifests
verify_cluster
# ... rest of function ...
}
```
## Resultaten
### ✅ Succesvolle Oplossingen
1. **Cluster Creatie**: Kind clusters worden nu succesvol aangemaakt
2. **Node Status**: Control-plane nodes bereiken `Ready` status
3. **CRI Functionaliteit**: Container runtime communiceert correct met kubelet
4. **Basis Kubernetes Operaties**: Deployments, services, en pods werken correct
### ⚠️ Resterende Beperkingen
**Ingress Controller Probleem**: De NGINX Ingress controller ondervindt nog steeds "too many open files" fouten vanwege file descriptor beperkingen die niet kunnen worden aangepast binnen de Kind container omgeving.
**Foutmelding**:
```
too many open files
```
**Oorzaak**: Dit is een beperking van de Kind/Podman setup waar kernel parameters niet kunnen worden aangepast vanuit containers.
## Troubleshooting Commands
### Diagnose Commands
```bash
# Controleer containerd status
ssh minty "podman exec eveai-dev-cluster-control-plane systemctl status containerd"
# Bekijk containerd logs
ssh minty "podman exec eveai-dev-cluster-control-plane journalctl -u containerd -f"
# Test CRI connectiviteit
ssh minty "podman exec eveai-dev-cluster-control-plane crictl version"
# Controleer file descriptor usage
ssh minty "podman exec eveai-dev-cluster-control-plane sh -c 'lsof | wc -l'"
# Controleer node status
kubectl get nodes -o wide
# Controleer kubelet logs
ssh minty "podman exec eveai-dev-cluster-control-plane journalctl -u kubelet --no-pager -n 20"
```
### Cluster Management
```bash
# Cluster verwijderen (met Podman provider)
KIND_EXPERIMENTAL_PROVIDER=podman kind delete cluster --name eveai-dev-cluster
# Nieuwe cluster aanmaken
cd /path/to/k8s/dev && ./setup-dev-cluster.sh
# Cluster status controleren
kubectl get all -n eveai-dev
```
## Preventieve Maatregelen
### 1. Configuratie Validatie
- **Minimale Kind Configuratie**: Gebruik alleen noodzakelijke kubeadmConfigPatches
- **Stapsgewijze Uitbreiding**: Voeg complexe configuraties geleidelijk toe
- **Testing**: Test elke configuratiewijziging in isolatie
### 2. Monitoring
- **Health Checks**: Implementeer uitgebreide CRI status controles
- **Logging**: Monitor containerd en kubelet logs voor vroege waarschuwingen
- **Automatische Recovery**: Implementeer automatische herstart procedures
### 3. Documentatie
- **Configuratie Geschiedenis**: Documenteer alle configuratiewijzigingen
- **Troubleshooting Procedures**: Onderhoud actuele troubleshooting guides
- **Known Issues**: Bijhouden van bekende beperkingen en workarounds
## Aanbevelingen voor Productie
### 1. Infrastructure Alternatieven
Voor productie-omgevingen waar Ingress controllers essentieel zijn:
- **Volledige VM Setup**: Gebruik echte virtuele machines waar kernel parameters kunnen worden geconfigureerd
- **Bare-metal Kubernetes**: Implementeer op fysieke hardware voor volledige controle
- **Managed Kubernetes**: Overweeg cloud-managed solutions (EKS, GKE, AKS)
### 2. Host-level Configuratie
```bash
# Op de host (minty) machine
sudo mkdir -p /etc/systemd/system/user@.service.d/
sudo tee /etc/systemd/system/user@.service.d/limits.conf << EOF
[Service]
LimitNOFILE=1048576
LimitNPROC=1048576
EOF
sudo systemctl daemon-reload
```
### 3. Alternatieve Ingress Controllers
Test andere ingress controllers die mogelijk lagere file descriptor vereisten hebben:
- **Traefik**
- **HAProxy Ingress**
- **Istio Gateway**
## Conclusie
De containerd CRI plugin failure is succesvol opgelost door:
1. **Vereenvoudiging** van de Kind cluster configuratie
2. **Implementatie** van container resource limits configuratie
3. **Toevoeging** van uitgebreide CRI status verificatie
4. **Verbetering** van error handling en diagnostics
Het cluster is nu volledig functioneel voor basis Kubernetes operaties. De resterende Ingress controller beperking is een bekende limitatie van de Kind/Podman omgeving en vereist alternatieve oplossingen voor productie gebruik.
## Bijlagen
### A. Gewijzigde Bestanden
- `k8s/dev/setup-dev-cluster.sh` - Toegevoegde functies en verbeterde workflow
- `k8s/dev/kind-dev-cluster.yaml` - Vereenvoudigde configuratie
- `k8s/dev/kind-minimal.yaml` - Nieuwe minimale test configuratie
### B. Tijdsinschatting Oplossing
- **Probleem Identificatie**: 2-3 uur
- **Root Cause Analyse**: 1-2 uur
- **Oplossing Implementatie**: 2-3 uur
- **Testing en Verificatie**: 1-2 uur
- **Documentatie**: 1 uur
- **Totaal**: 7-11 uur
### C. Lessons Learned
1. **Complexiteit Vermijden**: Start met minimale configuraties en bouw geleidelijk uit
2. **Systematische Diagnose**: Gebruik gestructureerde troubleshooting approaches
3. **Environment Beperkingen**: Begrijp de beperkingen van containerized Kubernetes (Kind)
4. **Monitoring Essentieel**: Implementeer uitgebreide health checks en logging
5. **Documentatie Cruciaal**: Documenteer alle wijzigingen en procedures voor toekomstig gebruik

View File

@@ -0,0 +1,202 @@
# Evie Object Storage Governance (Optie 3)
**Doel:** 1 bucket per omgeving (staging / prod), met **prefixen per
tenant**. Duidelijke scheiding van datatypes (documents vs assets), lage
beheerlast, goed schaalbaar.
------------------------------------------------------------------------
## 1) Structuur & naamgeving
### Buckets (per omgeving)
- **staging:** `evie-staging`
- **prod:** `evie-prod`
> Buckets zijn S3-compatibel op Scaleway
> (`https://s3.<regio>.scw.cloud`). Houd buckets "plat" (alle tenants
> als prefix).
### Prefix layout (per tenant)
<bucket>/
tenant-<tenantId>/
documents/
<subfolders naar keuze>...
assets/
<subfolders naar keuze>...
**Conventies** - **Tenant prefix:** `tenant-<tenantId>` (tenantId =
interne stabiele sleutel; geen PII). - **Datatypes:** `documents/` en
`assets/` (harde scheiding). - **Bestandsnamen:** `snake-case` of
`kebab-case`; voeg optioneel datum/uuid toe bij uploads die kunnen
conflicteren.
------------------------------------------------------------------------
## 2) Toegang & secrets
### IAM-model
- **Één IAM Application per omgeving**
- `evie-staging-app` → keys in **staging** k8s Secret\
- `evie-prod-app` → keys in **prod** k8s Secret\
- Toegang **alleen** tot het eigen bucket (`evie-staging` of
`evie-prod`).
### App-side secrets (env)
- `S3_ENDPOINT=https://s3.<regio>.scw.cloud`
- `S3_BUCKET=evie-<env>`
- `S3_ACCESS_KEY=***`
- `S3_SECRET_KEY=***`
- `S3_REGION=<regio>` (bv. `fr-par`)
- (optioneel) `S3_FORCE_PATH_STYLE=false`
> **Presigned uploads**: genereer **server-side** presigned URL's per
> tenant/prefix; geef nooit de master-keys aan de client.
------------------------------------------------------------------------
## 3) Policies (conceptueel)
- **Bucket policy**: sta alleen requests toe met geldige credentials
van de Evie-app van die omgeving.
- **Prefix scope** (in app-logica): alle reads/writes **moeten** met
pad beginnen op `tenant-<tenantId>/...`.
- **Optioneel** (later): extra policy-groepen voor specifieke
workflows (vb. tijdelijke ingest job).
> **Belangrijk:** autorisatie op tenantniveau afdwingen in **je
> applicatie** (context = `tenantId`). Nooit paden samenstellen vanuit
> user input zonder whitelisting/validation.
------------------------------------------------------------------------
## 4) Lifecycle & retentie
**Doel:** kosten beheersen, assets sneller "kouder", documenten langer
bewaren.
-----------------------------------------------------------------------
Scope (Filter) Regel
----------------------------------- -----------------------------------
`tenant-*/assets/` → One Zone-IA na 30 dagen
`tenant-*/assets/` → Glacier/Archive na 180 dagen
(optioneel)
`tenant-*/documents/` → Standard (geen transition) of IA
na 180d
`tenant-*/documents/` (tijdelijke Expire (delete) na 7--14 dagen
previews)
-----------------------------------------------------------------------
> Lifecycle definieer je **per bucket** met **prefix filters**, zodat
> regels verschillend zijn voor `assets/` en `documents/`.
------------------------------------------------------------------------
## 5) CORS & distributie
- **CORS**: indien browser direct upload/download doet, whitelist de
domeinen van je app (origin), en methodes `GET, PUT, POST`. Alleen
benodigde headers toestaan.
- **Publieke distributie** (indien nodig):
- Kleine public-reads via presigned URL's (aanbevolen).\
- Of activeer publieke read op een **specifieke** `public/`-prefix
(niet op de hele bucket).\
- Overweeg een CDN/edge-lag via Scaleway Edge Services voor
veelgevraagde assets.
------------------------------------------------------------------------
## 6) Observability & beheer
- **Logging/metrics**:
- App: log alle S3 calls met `tenantId` + object key.\
- Platform: gebruik Scaleway Cockpit voor capacity & request
metrics.
- **Quota & limieten**:
- 1 bucket per omgeving beperkt "bucket-sprawl".\
- Objecten en totale grootte zijn praktisch onbeperkt; plan wel
lifecycle om groei te managen.
- **Monitoring**:
- Alerts op snelgroeiende **assets**-prefixen, high error rates
(4xx/5xx), en mislukte lifecycle-transities.
------------------------------------------------------------------------
## 7) Operationele workflows
### Tenant aanmaken
1. DB schema provisionen.
2. (S3) **Geen nieuwe bucket**; enkel **prefix**:
`tenant-<id>/documents/` en `tenant-<id>/assets/` zijn impliciet.
3. (Optioneel) Init bestanden/placeholder objecten.
4. App-config linkt de tenant aan zijn prefix (centrale mapping).
### Upload (app -\> S3)
1. App valideert `tenantId` en datatype (`documents|assets`).\
2. App construeert **canonical path**: `tenant-<id>/<datatype>/<...>`\
3. App genereert **presigned PUT** (tijdelijk) en geeft terug aan
frontend.\
4. Frontend uploadt rechtstreeks naar S3 met presigned URL.
### Download / Serve
- Interne downloads: app signed GET of server-side stream.\
- Externe/public: **presigned GET** met korte TTL of via public-only
prefix + CDN.
### Opruimen & lifecycle
- Tijdelijke artefacten: app scheduled cleanup (of lifecycle
"Expiration").\
- Archivering: lifecycle transitions per prefix.
------------------------------------------------------------------------
## 8) Beveiliging
- **Least privilege**: IAM-keys enkel voor het bucket van de
omgeving.\
- **Encryptie**: server-side encryption (default) volstaat vaak;
overweeg KMS als apart key-beleid nodig is.\
- **Auditing**: log alle **write**-operaties met gebruikers- en
tenantcontext.\
- **Backups**: documenten zijn de "bron"? Zo ja, S3 is primaire opslag
en RAG-index kan herbouwd worden. Anders: definieer
export/replica-strategie.
------------------------------------------------------------------------
## 9) Migratie van MinIO → Scaleway
1. **Freeze window** (kort): pauzeer uploads of werk met **duale
write** (MinIO + S3) gedurende migratie.\
2. **Sync**: gebruik `rclone` of `mc mirror` om
`minio://bucket/tenant-*/{documents,assets}/`
`s3://evie-<env>/tenant-*/...`.\
3. **Verifieer**: random checksums / sample reads per tenant.\
4. **Switch**: zet `S3_ENDPOINT` en keys naar Scaleway; laat nieuwe
writes enkel naar S3 gaan.\
5. **Decom**: na grace-periode MinIO uitfaseren.
------------------------------------------------------------------------
## 10) Checklist (TL;DR)
- [ ] Buckets: `evie-staging`, `evie-prod`.\
- [ ] Prefix: `tenant-<id>/{documents,assets}/`.\
- [ ] IAM: 1 Application per omgeving; keys in k8s Secret.\
- [ ] Policy: alleen app-toegang; app dwingt prefix-scope per tenant
af.\
- [ ] Lifecycle: assets sneller koud, docs langer.\
- [ ] CORS: alleen noodzakelijke origins/methods.\
- [ ] Presigned URLs voor browser interacties.\
- [ ] Logging/metrics/alerts ingericht.\
- [ ] Migratiepad van MinIO uitgewerkt en getest.

View File

@@ -0,0 +1,161 @@
graph TB
%% Host Machine
subgraph "Host Machine (macOS)"
HOST[("Host Machine<br/>macOS Sonoma")]
PODMAN[("Podman<br/>Container Runtime")]
HOSTDIRS[("Host Directories<br/>~/k8s-data/dev/<br/>• minio<br/>• redis<br/>• logs<br/>• prometheus<br/>• grafana<br/>• certs")]
end
%% Kind Cluster
subgraph "Kind Cluster (eveai-dev-cluster)"
%% Control Plane
CONTROL[("Control Plane Node<br/>Port Mappings:<br/>• 80:30080<br/>• 443:30443<br/>• 3080:30080")]
%% Ingress Controller
subgraph "ingress-nginx namespace"
INGRESS[("NGINX Ingress Controller<br/>Handles routing to services")]
end
%% EveAI Dev Namespace
subgraph "eveai-dev namespace"
%% Web Services
subgraph "Web Services"
APP[("EveAI App<br/>Port: 5001<br/>NodePort: 30001")]
API[("EveAI API<br/>Port: 5003<br/>NodePort: 30003")]
CHAT[("EveAI Chat Client<br/>Port: 5004<br/>NodePort: 30004")]
STATIC[("Static Files Service<br/>NGINX<br/>Port: 80")]
end
%% Background Services
subgraph "Background Workers"
WORKERS[("EveAI Workers<br/>Replicas: 2<br/>Celery Workers")]
CHATWORKERS[("EveAI Chat Workers<br/>Replicas: 2<br/>Celery Workers")]
BEAT[("EveAI Beat<br/>Celery Scheduler<br/>Replicas: 1")]
ENTITLE[("EveAI Entitlements<br/>Port: 8000")]
end
%% Infrastructure Services
subgraph "Infrastructure Services"
REDIS[("Redis<br/>Port: 6379<br/>NodePort: 30379")]
MINIO[("MinIO<br/>Port: 9000<br/>Console: 9001<br/>NodePort: 30900")]
end
%% Monitoring Services
subgraph "Monitoring Stack"
PROM[("Prometheus<br/>Port: 9090")]
GRAFANA[("Grafana<br/>Port: 3000")]
NGINX_EXPORTER[("NGINX Prometheus Exporter<br/>Port: 9113")]
end
%% Storage
subgraph "Persistent Storage"
PV_REDIS[("Redis PV<br/>5Gi Local")]
PV_MINIO[("MinIO PV<br/>20Gi Local")]
PV_LOGS[("App Logs PV<br/>5Gi Local")]
PV_PROM[("Prometheus PV<br/>10Gi Local")]
PV_GRAFANA[("Grafana PV<br/>5Gi Local")]
end
%% Configuration
subgraph "Configuration"
CONFIGMAP[("eveai-config<br/>ConfigMap")]
SECRETS[("eveai-secrets<br/>Secret")]
end
end
end
%% External Registry
REGISTRY[("Container Registry<br/>registry.ask-eve-ai-local.com<br/>josakola/eveai_*")]
%% Connections
HOST --> PODMAN
PODMAN --> CONTROL
HOSTDIRS --> PV_REDIS
HOSTDIRS --> PV_MINIO
HOSTDIRS --> PV_LOGS
HOSTDIRS --> PV_PROM
HOSTDIRS --> PV_GRAFANA
%% Service connections
CONTROL --> INGRESS
INGRESS --> APP
INGRESS --> API
INGRESS --> CHAT
INGRESS --> STATIC
%% Worker connections to Redis
WORKERS --> REDIS
CHATWORKERS --> REDIS
BEAT --> REDIS
%% All services connect to storage
APP --> PV_LOGS
API --> PV_LOGS
CHAT --> PV_LOGS
WORKERS --> PV_LOGS
CHATWORKERS --> PV_LOGS
BEAT --> PV_LOGS
ENTITLE --> PV_LOGS
%% Infrastructure storage
REDIS --> PV_REDIS
MINIO --> PV_MINIO
PROM --> PV_PROM
GRAFANA --> PV_GRAFANA
%% Configuration connections
CONFIGMAP --> APP
CONFIGMAP --> API
CONFIGMAP --> CHAT
CONFIGMAP --> WORKERS
CONFIGMAP --> CHATWORKERS
CONFIGMAP --> BEAT
CONFIGMAP --> ENTITLE
SECRETS --> APP
SECRETS --> API
SECRETS --> CHAT
SECRETS --> WORKERS
SECRETS --> CHATWORKERS
SECRETS --> BEAT
SECRETS --> ENTITLE
%% Registry connections
REGISTRY --> APP
REGISTRY --> API
REGISTRY --> CHAT
REGISTRY --> WORKERS
REGISTRY --> CHATWORKERS
REGISTRY --> BEAT
REGISTRY --> ENTITLE
%% Monitoring connections
PROM --> APP
PROM --> API
PROM --> CHAT
PROM --> REDIS
PROM --> MINIO
PROM --> NGINX_EXPORTER
GRAFANA --> PROM
%% External Access
subgraph "External Access"
ACCESS[("http://minty.ask-eve-ai-local.com:3080<br/>• /admin/ → App<br/>• /api/ → API<br/>• /chat-client/ → Chat<br/>• /static/ → Static Files")]
end
ACCESS --> INGRESS
%% Styling
classDef webService fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef infrastructure fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
classDef storage fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
classDef monitoring fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef config fill:#fce4ec,stroke:#880e4f,stroke-width:2px
classDef external fill:#f1f8e9,stroke:#33691e,stroke-width:2px
class APP,API,CHAT,STATIC webService
class REDIS,MINIO,WORKERS,CHATWORKERS,BEAT,ENTITLE infrastructure
class PV_REDIS,PV_MINIO,PV_LOGS,PV_PROM,PV_GRAFANA,HOSTDIRS storage
class PROM,GRAFANA,NGINX_EXPORTER monitoring
class CONFIGMAP,SECRETS config
class REGISTRY,ACCESS external

View File

@@ -40,10 +40,10 @@ window.EveAI.JsonEditors = {
mainMenuBar: options.mainMenuBar !== undefined ? options.mainMenuBar : true, mainMenuBar: options.mainMenuBar !== undefined ? options.mainMenuBar : true,
navigationBar: options.navigationBar !== undefined ? options.navigationBar : false, navigationBar: options.navigationBar !== undefined ? options.navigationBar : false,
statusBar: options.statusBar !== undefined ? options.statusBar : !isReadOnly, statusBar: options.statusBar !== undefined ? options.statusBar : !isReadOnly,
onChange: (updatedContent, previousContent, { contentErrors, patchResult }) => { onChange: options.onChange || ((updatedContent, previousContent, { contentErrors, patchResult }) => {
// content is an object { json: unknown } | { text: string } // Default onChange behavior - alleen loggen
console.log('onChange', { updatedContent, previousContent, contentErrors, patchResult }) console.log('onChange', { updatedContent, previousContent, contentErrors, patchResult });
} })
}; };
console.log('EditorProps', editorProps); console.log('EditorProps', editorProps);
@@ -107,13 +107,51 @@ document.addEventListener('DOMContentLoaded', function() {
window.EveAI.JsonEditors.initialize(containerId, data, { window.EveAI.JsonEditors.initialize(containerId, data, {
mode: isReadOnly ? 'preview' : 'tree', mode: isReadOnly ? 'preview' : 'tree',
readOnly: isReadOnly, readOnly: isReadOnly,
onChangeText: isReadOnly ? undefined : (jsonString) => { textarea.value = jsonString; } onChange: isReadOnly ? undefined : (updatedContent, previousContent, { contentErrors, patchResult }) => {
// Automatische synchronisatie naar textarea bij elke wijziging
if (updatedContent.json !== undefined) {
textarea.value = JSON.stringify(updatedContent.json, null, 2);
} else if (updatedContent.text !== undefined) {
textarea.value = updatedContent.text;
}
console.log('Textarea automatisch bijgewerkt via onChange');
}
}); });
} catch (e) { } catch (e) {
console.error('Error parsing initial JSON for .json-editor:', e); console.error('Error parsing initial JSON for .json-editor:', e);
container.innerHTML = `<div class="alert alert-danger p-3"><strong>Error loading JSON data:</strong><br>${e.message}</div>`; container.innerHTML = `<div class="alert alert-danger p-3"><strong>Error loading JSON data:</strong><br>${e.message}</div>`;
} }
}); });
// Real-time synchronisatie als extra beveiliging
setInterval(function() {
document.querySelectorAll('.json-editor').forEach(function(textarea) {
if (textarea.style.display === 'none') { // Alleen voor verborgen textareas (zoals in edit forms)
const containerId = textarea.id + '-editor';
const editor = window.EveAI?.JsonEditors?.get(containerId);
if (editor && editor.get) {
try {
const content = editor.get();
let newValue = '';
if (content.json !== undefined) {
newValue = JSON.stringify(content.json, null, 2);
} else if (content.text !== undefined) {
newValue = content.text;
}
// Alleen updaten als de waarde daadwerkelijk is veranderd
if (textarea.value !== newValue) {
textarea.value = newValue;
console.log('Real-time sync uitgevoerd voor', textarea.id);
}
} catch (e) {
// Stil falen - geen console spam
}
}
}
});
}, 1000); // Elke seconde controleren
// Alleen-lezen containers // Alleen-lezen containers
document.querySelectorAll('.json-viewer').forEach(function(container) { document.querySelectorAll('.json-viewer').forEach(function(container) {
const dataElement = document.getElementById(container.id + '-data'); const dataElement = document.getElementById(container.id + '-data');

View File

@@ -77,122 +77,42 @@
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const textareaElement = document.getElementById('json_content'); const textareaElement = document.getElementById('json_content');
let currentEditor = null;
// Wacht even en probeer dan de editor te krijgen via de EveAI namespace
setTimeout(function() {
currentEditor = window.EveAI?.JsonEditors?.get('json_content-editor');
if (currentEditor) {
console.log('JSON Editor gevonden en gekoppeld');
} else {
console.log('JSON Editor nog niet beschikbaar, probeer handmatige initialisatie');
// Probeer handmatige initialisatie als fallback
if (window.EveAI?.JsonEditors?.initialize) {
try {
const initialContent = JSON.parse(textareaElement.value);
currentEditor = window.EveAI.JsonEditors.initialize('json_content-editor', initialContent, {
mode: 'tree',
readOnly: false,
mainMenuBar: true,
navigationBar: false,
statusBar: true,
onChange: (updatedContent, previousContent, { contentErrors, patchResult }) => {
console.log('Editor content changed');
// Automatisch de textarea updaten bij wijzigingen
syncEditorToTextarea();
}
});
} catch (e) {
console.error('Error bij handmatige initialisatie:', e);
}
}
}
}, 500);
// Functie om editor inhoud naar textarea te synchroniseren // Functie om editor inhoud naar textarea te synchroniseren
function syncEditorToTextarea() { function syncEditorToTextarea() {
if (currentEditor && currentEditor.get) { const editor = window.EveAI?.JsonEditors?.get('json_content-editor');
if (editor && editor.get) {
try { try {
const content = currentEditor.get(); const content = editor.get();
if (content.json !== undefined) { if (content.json !== undefined) {
textareaElement.value = JSON.stringify(content.json, null, 2); textareaElement.value = JSON.stringify(content.json, null, 2);
} else if (content.text !== undefined) { } else if (content.text !== undefined) {
textareaElement.value = content.text; textareaElement.value = content.text;
} }
console.log('Editor content gesynchroniseerd naar textarea'); console.log('Editor content gesynchroniseerd naar textarea');
return true;
} catch (e) { } catch (e) {
console.error('Error bij synchronisatie:', e); console.error('Error bij synchronisatie:', e);
return false;
} }
} }
return false;
} }
// Sync knop // Form submission validation en synchronisatie
document.getElementById('getEditorContentBtn').addEventListener('click', function() {
syncEditorToTextarea();
alert('Editor inhoud gesynchroniseerd naar textarea');
});
// Validate JSON button
document.getElementById('validateJsonBtn').addEventListener('click', function() {
// Eerst synchroniseren
syncEditorToTextarea();
try {
const content = textareaElement.value;
JSON.parse(content);
// Show success message
if (typeof Swal !== 'undefined') {
Swal.fire({
title: 'Geldig JSON',
text: 'De JSON syntax is correct!',
icon: 'success',
timer: 2000,
showConfirmButton: false
});
} else {
alert('De JSON syntax is correct!');
}
} catch (e) {
// Show error message
if (typeof Swal !== 'undefined') {
Swal.fire({
title: 'Ongeldig JSON',
text: 'JSON syntax fout: ' + e.message,
icon: 'error',
confirmButtonText: 'OK'
});
} else {
alert('JSON syntax fout: ' + e.message);
}
}
});
// Form submission validation
document.getElementById('editAssetForm').addEventListener('submit', function(e) { document.getElementById('editAssetForm').addEventListener('submit', function(e) {
// Eerst de editor content synchroniseren // Eerst de editor content synchroniseren
syncEditorToTextarea(); if (!syncEditorToTextarea()) {
console.warn('Synchronisatie gefaald, maar probeer toch door te gaan');
}
try { try {
const content = textareaElement.value; const content = textareaElement.value;
JSON.parse(content); JSON.parse(content);
return true; // JSON is valid, allow submission
// JSON is valid, allow submission
return true;
} catch (error) { } catch (error) {
e.preventDefault(); e.preventDefault();
alert('Kan het formulier niet verzenden: JSON syntax fout - ' + error.message);
if (typeof Swal !== 'undefined') {
Swal.fire({
title: 'Ongeldig JSON',
text: 'Kan het formulier niet verzenden: JSON syntax fout - ' + error.message,
icon: 'error',
confirmButtonText: 'OK'
});
} else {
alert('Kan het formulier niet verzenden: JSON syntax fout - ' + error.message);
}
return false; return false;
} }
}); });

View File

@@ -21,6 +21,7 @@ from common.extensions import db, cache_manager
from common.models.user import SpecialistMagicLinkTenant from common.models.user import SpecialistMagicLinkTenant
from common.services.interaction.specialist_services import SpecialistServices from common.services.interaction.specialist_services import SpecialistServices
from common.utils.execution_progress import ExecutionProgressTracker from common.utils.execution_progress import ExecutionProgressTracker
from common.utils.minio_utils import MIB_CONVERTOR
from common.utils.model_logging_utils import set_logging_information, update_logging_information from common.utils.model_logging_utils import set_logging_information, update_logging_information
from common.utils.middleware import mw_before_request from common.utils.middleware import mw_before_request
@@ -870,8 +871,11 @@ def edit_asset(asset_id):
try: try:
# Haal de bewerkte JSON data op uit het formulier # Haal de bewerkte JSON data op uit het formulier
json_data = request.form.get('json_content') json_data = request.form.get('json_content')
current_app.logger.debug(f"Raw JSON data received for asset {asset_id}: {json_data[:200] if json_data else 'None'}...")
current_app.logger.debug(f"JSON data length: {len(json_data) if json_data else 0}")
if not json_data: if not json_data:
current_app.logger.error(f"No JSON data received for asset {asset_id}")
flash('Geen JSON data ontvangen', 'error') flash('Geen JSON data ontvangen', 'error')
return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id)) return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id))
@@ -892,7 +896,7 @@ def edit_asset(asset_id):
) )
# Update asset metadata # Update asset metadata
asset.file_size = file_size asset.file_size = file_size / MIB_CONVERTOR
asset.updated_at = dt.now(tz.utc) asset.updated_at = dt.now(tz.utc)
asset.updated_by = current_user.id asset.updated_by = current_user.id
asset.last_used_at = dt.now(tz.utc) asset.last_used_at = dt.now(tz.utc)

View File

@@ -572,15 +572,12 @@ def tenant_make():
form = TenantMakeForm() form = TenantMakeForm()
customisation_config = cache_manager.customisations_config_cache.get_config("CHAT_CLIENT_CUSTOMISATION") customisation_config = cache_manager.customisations_config_cache.get_config("CHAT_CLIENT_CUSTOMISATION")
default_customisation_options = create_default_config_from_type_config(customisation_config["configuration"]) default_customisation_options = create_default_config_from_type_config(customisation_config["configuration"])
form.add_dynamic_fields("configuration", customisation_config, default_customisation_options)
if form.validate_on_submit(): if form.validate_on_submit():
tenant_id = session['tenant']['id'] tenant_id = session['tenant']['id']
new_tenant_make = TenantMake() new_tenant_make = TenantMake()
form.populate_obj(new_tenant_make) form.populate_obj(new_tenant_make)
new_tenant_make.tenant_id = tenant_id new_tenant_make.tenant_id = tenant_id
customisation_options = form.get_dynamic_data("configuration")
new_tenant_make.chat_customisation_options = json.dumps(customisation_options)
# Verwerk allowed_languages als array # Verwerk allowed_languages als array
new_tenant_make.allowed_languages = form.allowed_languages.data if form.allowed_languages.data else None new_tenant_make.allowed_languages = form.allowed_languages.data if form.allowed_languages.data else None
set_logging_information(new_tenant_make, dt.now(tz.utc)) set_logging_information(new_tenant_make, dt.now(tz.utc))
@@ -589,9 +586,10 @@ def tenant_make():
db.session.add(new_tenant_make) db.session.add(new_tenant_make)
db.session.commit() db.session.commit()
flash('Tenant Make successfully added!', 'success') flash('Tenant Make successfully added!', 'success')
current_app.logger.info(f'Tenant Make {new_tenant_make.name} successfully added for tenant {tenant_id}!') current_app.logger.info(f'Tenant Make {new_tenant_make.name}, id {new_tenant_make.id} successfully added '
f'for tenant {tenant_id}!')
# Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type) # Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type)
return redirect(prefixed_url_for('user_bp.tenant_makes', tenant_make_id=new_tenant_make.id)) return redirect(prefixed_url_for('user_bp.edit_tenant_make', tenant_make_id=new_tenant_make.id))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to add Tenant Make. Error: {e}', 'danger') flash(f'Failed to add Tenant Make. Error: {e}', 'danger')

View File

@@ -50,6 +50,29 @@ START_SELECTION_QUESTIONS = [
"Would you be open to answering a few questions to learn more about the role and your fit?", "Would you be open to answering a few questions to learn more about the role and your fit?",
"Would you like to continue and start the first part of the application journey?" "Would you like to continue and start the first part of the application journey?"
] ]
START_SELECTION_QUESTIONS_CTD = [
"Do you have any other questions, or shall we start the interview to see if theres a match with the job?",
"Do you have any questions, or shall we begin to explore if this job suits you?",
"Would you like to know anything else first, or shall we start the interview to see if the role feels like a good fit?",
"Are there any remaining questions, or shall we find out whether this job is something for you?",
"Is there anything still on your mind, or shall we begin the conversation to explore the match?",
"Feel free to ask if you still have any questions; otherwise, shall we begin the interview?",
"Is everything clear for you, or shall we now take a look together to see if this role is made for you?",
"Are you ready, or is there anything else youd like clarified before we get started?",
"No more questions, or shall we start discovering if theres a connection with this role?",
"Are you ready to begin and see if you and this job are a good match?",
# Extra variants
"Before we move on, is there anything youd like to ask? Or shall we begin?",
"Shall we get started, or is there something else youd like to clarify first?",
"Would you like to go over anything else before we start the interview?",
"Is now a good time to begin, or do you have more questions before we proceed?",
"Do you feel ready to start, or should we address any remaining questions first?",
"Would you prefer to discuss any final points, or shall we begin the selection process?",
"Is there anything we should clear up before starting, or shall we dive right in?",
"Would you like to ask anything else before we begin exploring the role together?",
"Do you want to go over anything else first, or shall we kick off the interview?",
"Are we good to start, or is there anything else youd like to cover?"
]
TRY_TO_START_SELECTION_QUESTIONS = [ TRY_TO_START_SELECTION_QUESTIONS = [
"That's a pity — we can only move forward if we start the selection process. Would you like to begin now?", "That's a pity — we can only move forward if we start the selection process. Would you like to begin now?",
"We understand, though its worth mentioning that the only way to continue is to start the procedure. Shall we get started after all?", "We understand, though its worth mentioning that the only way to continue is to start the procedure. Shall we get started after all?",
@@ -83,7 +106,17 @@ KO_CRITERIA_NOT_MET_MESSAGES = [
"Thanks so much for answering our questions. This role may not be the right fit, but wed love for you to consider applying again when new positions become available.", "Thanks so much for answering our questions. This role may not be the right fit, but wed love for you to consider applying again when new positions become available.",
"We value your interest in this position. While we wont be moving forward in this case, we warmly invite you to explore other roles with us in the future.", "We value your interest in this position. While we wont be moving forward in this case, we warmly invite you to explore other roles with us in the future.",
"Your input has been very helpful. Although we're not proceeding at this time, we thank you for your interest and hope to see you again for other opportunities.", "Your input has been very helpful. Although we're not proceeding at this time, we thank you for your interest and hope to see you again for other opportunities.",
"Thank you for taking part in the process. We wont continue with your application for this role, but we invite you to stay informed about future openings through our website." "Thank you for taking part in the process. We wont continue with your application for this role, but we invite you to stay informed about future openings through our website.",
# Nieuwe varianten
"Thank you for providing some additional clarification. For this role, the previously mentioned requirement remains essential. We hope youll continue to follow our other vacancies!",
"We appreciate your engagement. For this specific role, we do have to end the process here. Wishing you the best of luck with your next step!",
"Thank you for your additional information. Unfortunately, this does not change the selection process for this position. We look forward to possibly meeting you again in the future!",
"Thank you for taking the time to respond. For this vacancy, we can only proceed with candidates who meet all the requirements. We wish you all the very best!",
"Weve reviewed your answers carefully. Unfortunately, we cant continue with your application for this role, but we encourage you to check our site for future openings.",
"Were grateful for your time and interest. Sadly, this position requires criteria that werent met, but wed love to see your application for other roles.",
"Thank you for sharing more details. For this specific position, the original requirements still apply. Please keep an eye out for roles that might suit you better.",
"We value the effort youve put into this process. While we cant move forward this time, wed be happy to see your application for future opportunities.",
"Your answers gave us a good understanding of your background. Unfortunately, we cant proceed with this position, but we hope to connect again in the future."
] ]
KO_CRITERIA_MET_MESSAGES = [ KO_CRITERIA_MET_MESSAGES = [
"Thank you for your answers. They correspond to some key elements of the role. Would you be open to sharing your contact details so we can continue the selection process?", "Thank you for your answers. They correspond to some key elements of the role. Would you be open to sharing your contact details so we can continue the selection process?",
@@ -608,7 +641,7 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
question = None question = None
match self.previous_interview_phase: match self.previous_interview_phase:
case "start_selection_procedure": case "start_selection_procedure":
question = random.choice(START_SELECTION_QUESTIONS) question = random.choice(START_SELECTION_QUESTIONS_CTD)
case "personal_contact_data_preparation": case "personal_contact_data_preparation":
question = random.choice(CONTACT_DATA_QUESTIONS) question = random.choice(CONTACT_DATA_QUESTIONS)
case "candidate_selected": case "candidate_selected":

View File

@@ -0,0 +1,327 @@
# Kubernetes Service Management System
## Overview
This implementation provides a comprehensive Kubernetes service management system inspired by your `podman_env_switch.sh` workflow. It allows you to easily manage EveAI services across different environments with simple, memorable commands.
**✅ Latest Update (August 2025):** The system has been enhanced with structured deployment functionality, consolidating all features from `deploy-all-services.sh` into the main `k8s_env_switch.sh` system. This eliminates duplicate maintenance and provides a unified interface for all service management operations.
## 🚀 Quick Start
```bash
# Switch to dev environment
source k8s/k8s_env_switch.sh dev
# Structured deployment (recommended - replaces deploy-all-services.sh)
kup-all-structured
# Test connectivity and show connection info
ktest
kinfo
# Traditional service group management
kup apps
kps
# Individual service management
kup-api
kup-workers
# Stop services (keeping data)
kdown apps
# View logs
klogs eveai-app
```
## 📁 File Structure
```
k8s/
├── k8s_env_switch.sh # Main script (like podman_env_switch.sh)
├── scripts/
│ ├── k8s-functions.sh # Core service management functions (enhanced)
│ ├── service-groups.sh # Service group definitions
│ ├── dependency-checks.sh # Dependency validation
│ └── logging-utils.sh # Logging utilities
├── dev/ # Dev environment configs
│ ├── setup-dev-cluster.sh # Cluster setup script
│ ├── deploy-all-services.sh # Legacy script (functionality moved to k8s_env_switch.sh)
│ └── *.yaml # Service configurations
└── test-k8s-functions.sh # Test script
```
### 🔄 Consolidation Benefits
- **✅ No Duplicate Maintenance** - Single system for all service management
- **✅ Enhanced Functionality** - All deploy-all-services.sh features integrated
- **✅ Consistent Interface** - Unified command structure across operations
- **✅ Better User Experience** - Clear, memorable commands with comprehensive help
- **✅ Future-Ready** - Multi-environment support and extensibility
## 🔧 Environment Setup
### Supported Environments
- `dev` - Development (current focus)
- `test` - Testing (future)
- `bugfix` - Bug fixes (future)
- `integration` - Integration testing (future)
- `prod` - Production (future)
### Environment Variables Set
- `K8S_ENVIRONMENT` - Current environment
- `K8S_VERSION` - Service version
- `K8S_CLUSTER` - Cluster name
- `K8S_NAMESPACE` - Kubernetes namespace
- `K8S_CONFIG_DIR` - Configuration directory
- `K8S_LOG_DIR` - Log directory
## 📋 Service Groups
### Infrastructure
- `redis` - Redis cache
- `minio` - MinIO object storage
### Apps (Individual Management)
- `eveai-app` - Main application
- `eveai-api` - API service
- `eveai-chat-client` - Chat client
- `eveai-workers` - Celery workers (2 replicas)
- `eveai-chat-workers` - Chat workers (2 replicas)
- `eveai-beat` - Celery scheduler
- `eveai-entitlements` - Entitlements service
### Static
- `static-files` - Static file server
- `eveai-ingress` - Ingress controller
### Monitoring
- `prometheus` - Metrics collection
- `grafana` - Dashboards
- `flower` - Celery monitoring
## 🎯 Core Commands
### Structured Deployment (Recommended)
```bash
kup-all-structured # Deploy all services in structured order (replaces deploy-all-services.sh)
ktest # Test service connectivity via Ingress
kinfo # Show connection information and service URLs
```
### Service Group Management
```bash
kup [group] # Start service group
kdown [group] # Stop service group, keep data
kstop [group] # Stop service group without removal
kstart [group] # Start stopped service group
krefresh [group] # Restart service group
```
**Groups:** `infrastructure`, `apps`, `static`, `monitoring`, `all`
### Individual App Service Management
```bash
# Start individual services
kup-app # Start eveai-app
kup-api # Start eveai-api
kup-chat-client # Start eveai-chat-client
kup-workers # Start eveai-workers
kup-chat-workers # Start eveai-chat-workers
kup-beat # Start eveai-beat
kup-entitlements # Start eveai-entitlements
# Stop individual services
kdown-app # Stop eveai-app (keep data)
kstop-api # Stop eveai-api (without removal)
kstart-workers # Start stopped eveai-workers
```
### Status & Monitoring
```bash
kps # Show service status overview
klogs [service] # View service logs
klogs eveai-app # View specific service logs
```
### Cluster Management
```bash
cluster-start # Start cluster
cluster-stop # Stop cluster (Kind limitation note)
cluster-delete # Delete cluster (with confirmation)
cluster-status # Show cluster status
```
## 🔍 Dependency Management
The system automatically checks dependencies:
### Infrastructure Dependencies
- All app services require `redis` and `minio` to be running
- Automatic checks before starting app services
### App Dependencies
- `eveai-workers` and `eveai-chat-workers` require `eveai-api`
- `eveai-beat` requires `redis`
- Dependency validation with helpful error messages
### Deployment Order
1. Infrastructure (redis, minio)
2. Core apps (eveai-app, eveai-api, eveai-chat-client, eveai-entitlements)
3. Workers (eveai-workers, eveai-chat-workers, eveai-beat)
4. Static files and ingress
5. Monitoring services
## 📝 Logging System
### Log Files (in `$HOME/k8s-logs/dev/`)
- `k8s-operations.log` - All operations
- `service-errors.log` - Error messages
- `kubectl-commands.log` - kubectl command history
- `dependency-checks.log` - Dependency validation results
### Log Management
```bash
# View recent logs (after sourcing the script)
show_recent_logs operations # Recent operations
show_recent_logs errors # Recent errors
show_recent_logs kubectl # Recent kubectl commands
# Clear logs
clear_logs all # Clear all logs
clear_logs errors # Clear error logs
```
## 💡 Usage Examples
### Daily Development Workflow
```bash
# Start your day
source k8s/k8s_env_switch.sh dev
# Check what's running
kps
# Start infrastructure if needed
kup infrastructure
# Start specific apps you're working on
kup-api
kup-app
# Check logs while developing
klogs eveai-api
# Restart a service after changes
kstop-api
kstart-api
# or
krefresh apps
# End of day - stop services but keep data
kdown all
```
### Debugging Workflow
```bash
# Check service status
kps
# Check dependencies
show_dependency_status
# View recent errors
show_recent_logs errors
# Check specific service details
show_service_status eveai-api
# Restart problematic service
krefresh apps
```
### Testing New Features
```bash
# Stop specific service
kdown-workers
# Deploy updated version
kup-workers
# Monitor logs
klogs eveai-workers
# Check if everything is working
kps
```
## 🔧 Integration with Existing Scripts
### Enhanced deploy-all-services.sh
The existing script can be extended with new options:
```bash
./deploy-all-services.sh --group apps
./deploy-all-services.sh --service eveai-api
./deploy-all-services.sh --check-deps
```
### Compatibility
- All existing scripts continue to work unchanged
- New system provides additional management capabilities
- Logging integrates with existing workflow
## 🧪 Testing
Run the test suite to validate functionality:
```bash
./k8s/test-k8s-functions.sh
```
The test validates:
- ✅ Environment switching
- ✅ Function definitions
- ✅ Service group configurations
- ✅ Basic command execution
- ✅ Logging system
- ✅ Dependency checking
## 🚨 Important Notes
### Kind Cluster Limitations
- Kind clusters cannot be "stopped", only deleted
- `cluster-stop` provides information about this limitation
- Use `cluster-delete` to completely remove a cluster
### Data Persistence
- `kdown` and `kstop` preserve all persistent data (PVCs)
- Only `--delete-all` mode removes deployments completely
- Logs are always preserved in `$HOME/k8s-logs/`
### Multi-Environment Support
- Currently focused on `dev` environment
- Framework ready for `test`, `bugfix`, `integration`, `prod`
- Environment-specific configurations will be created as needed
## 🎉 Benefits
### Familiar Workflow
- Commands mirror your `podman_env_switch.sh` pattern
- Short, memorable function names (`kup`, `kdown`, etc.)
- Environment switching with `source` command
### Individual Service Control
- Start/stop any app service independently
- Dependency checking prevents issues
- Granular control over your development environment
### Comprehensive Logging
- All operations logged for debugging
- Environment-specific log directories
- Easy access to recent operations and errors
### Production Ready
- Proper error handling and validation
- Graceful degradation when tools are missing
- Extensible for multiple environments
The system is now ready for use! Start with `source k8s/k8s_env_switch.sh dev` and explore the available commands.

45
k8s/deploy-static-files.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
set -e
# Deploy Static Files Script voor EveAI
# File: k8s/deploy-static-files.sh
ENVIRONMENT=${1:-dev}
DRY_RUN=${2}
# Configuratie
REMOTE_HOST="minty.ask-eve-ai-local.com"
BUILD_DIR="nginx/static"
CLUSTER_CONTEXT="kind-eveai-${ENVIRONMENT}-cluster"
NAMESPACE="eveai-${ENVIRONMENT}"
echo "🚀 Deploying static files to ${ENVIRONMENT} cluster on ${REMOTE_HOST}..."
# Check if build exists
if [ ! -d "$BUILD_DIR" ]; then
echo "❌ Build directory $BUILD_DIR not found."
echo " Please run: cd nginx && npm run build && cd .."
exit 1
fi
# Show what will be deployed
echo "📦 Static files to deploy:"
du -sh "$BUILD_DIR"
find "$BUILD_DIR" -type f | wc -l | xargs echo " Files:"
if [ "$DRY_RUN" = "--dry-run" ]; then
echo "🔍 DRY RUN - would deploy to $ENVIRONMENT cluster"
exit 0
fi
# Deploy via direct rsync to access pod
echo "🚀 Deploying via rsync to $REMOTE_HOST:3873..."
rsync -av --delete "$BUILD_DIR/" "rsync://$REMOTE_HOST:3873/static/"
echo "✅ Static files deployed to cluster"
# Optional: Restart nginx pods to clear caches
echo "🔄 Restarting nginx pods..."
ssh "$REMOTE_HOST" "kubectl --context=$CLUSTER_CONTEXT rollout restart deployment/static-files -n $NAMESPACE"
echo "✅ Deployment completed successfully!"

111
k8s/dev/config-secrets.yaml Normal file
View File

@@ -0,0 +1,111 @@
# ConfigMaps and Secrets for EveAI Dev Environment
# File: config-secrets.yaml
# Note: Namespace is now defined in separate namespace.yaml file
---
# Non-sensitive configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: eveai-config
namespace: eveai-dev
data:
# Database configuration (points to external PostgreSQL)
DB_HOST: "postgres-external" # Points to headless service with endpoints
DB_PORT: "5432"
DB_NAME: "eveai_dev"
DB_USER: "luke"
# Redis configuration (internal to cluster)
REDIS_URL: "redis-service"
REDIS_PORT: "6379"
# MinIO configuration (internal to cluster)
MINIO_ENDPOINT: "minio-service:9000"
MINIO_ACCESS_KEY: "minioadmin"
# Application settings
FLASK_ENV: "development"
FLASK_DEBUG: "true"
# Flower configuration
FLOWER_USER: "Felucia"
# Nginx configuration
NGINX_SERVER_NAME: "minty.ask-eve-ai-local.com localhost"
# CrewAI configuration
CREWAI_STORAGE_DIR: "/app/crewai_storage"
# Monitoring configuration
PUSH_GATEWAY_HOST: "pushgateway-service"
PUSH_GATEWAY_PORT: "9091"
# Email configuration
SW_EMAIL_SENDER: "admin_dev@mail.askeveai.be"
SW_EMAIL_NAME: "Evie Admin (dev)"
SW_PROJECT: "f282f55a-ea52-4538-a979-5bcb890717ab"
---
# Sensitive configuration
apiVersion: v1
kind: Secret
metadata:
name: eveai-secrets
namespace: eveai-dev
type: Opaque
data:
# Database password (base64 encoded)
DB_PASS: U2t5d2Fsa2VyIQ== # "Skywalker!"
# API Keys (base64 encoded)
OPENAI_API_KEY: c2stcHJvai04UjBqV3p3akw3UGVvUHlNaEpUWlQzQmxia0ZKTGI2SGZSR0JIcjljRVZGV0VoVTc=
GROQ_API_KEY: Z3NrX0dIZlRkcFlwbmFTS1pGSklzSlJBV0dkeWIzRlkzNWN2RjZBTHBMVThEYzR0SUZMVWZRNA==
MISTRAL_API_KEY: MGY0WmlRMWtJcGdJS1RIWDhkMGE4R09EMnZBZ1ZxRW4=
ANTHROPIC_API_KEY: c2stYW50LWFwaTAzLWMyVG1remJSZWVHaFhCTzVKeE5INkJKTnlsUkRvbmM5R21aZDBIZRbrvVyeWVrZWMyVHJ2eWVrZWMyVGpOeWVrZWMybYk95Z1k=
LANGCHAIN_API_KEY: bHN2Ml9za180ZmViMWU2MDVlNzA0MGFlYjM1N2M1OTAyNWZiZWEzMl9jNWU4NWVjNDEx
SERPER_API_KEY: ZTRjNTUzODU2ZDBlNmI1YTE3MWVjNWU2YjY5ZDg3NDI4NWI5YmFkZg==
# Application secrets
SECRET_KEY: OTc4NjdjMTQ5MWJlYTVlZTZhOGU4NDM2ZWIxMWJmMmJhNmE2OWZmNTNhYjFiMTdlY2JhNDUwZDBmMmU1NzJlMQ==
SECURITY_PASSWORD_SALT: MjI4NjE0ODU5NDM5MTIzMjY0MDM1NTY1NTY4NzYxNDMzNjA3MjM1
JWT_SECRET_KEY: YnNkTWttUThPYmZNRDUyeUFGZzR0cnJ2amdqTWh1SXFnMmZqRHBEL0pxdmdZMGNjQ2NtbHNFblZGbVI3OVdQaUxLRUEzaThhNXptZWp3TFpLbDR2OVE9PQ==
API_ENCRYPTION_KEY: eGZGNTM2OUlzcmVkU3JscllaUWtNOVpOcmZVQVNZWVM2VENjQVI5VUtqND0=
# MinIO secret
MINIO_SECRET_KEY: bWluaW9hZG1pbg== # "minioadmin"
# Flower password
FLOWER_PASSWORD: SmVsZW5z # "Jungles"
# Email configuration
SW_EMAIL_ACCESS_KEY: U0NXRk1ROTM3MkhONFlHS0YwNFNXMA==
SW_EMAIL_SECRET_KEY: ZWM4NDYwNGMtZTJkNC00YjBkLWExMjAtNDA0MjA2OTNmNDJh
---
# Headless Service for PostgreSQL (points to host database)
apiVersion: v1
kind: Service
metadata:
name: postgres-external
namespace: eveai-dev
spec:
type: ClusterIP
clusterIP: None
ports:
- port: 5432
targetPort: 5432
protocol: TCP
---
# Endpoints for PostgreSQL (points to host IP)
apiVersion: v1
kind: Endpoints
metadata:
name: postgres-external
namespace: eveai-dev
subsets:
- addresses:
- ip: 192.168.1.130 # Host IP where PostgreSQL is running
ports:
- port: 5432
protocol: TCP

View File

@@ -0,0 +1,66 @@
# EveAI Ingress Configuration for Dev Environment
# File: eveai-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: eveai-ingress
namespace: eveai-dev
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
spec:
rules:
- host: minty.ask-eve-ai-local.com
http:
paths:
# Static files - hoogste prioriteit
- path: /static(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: static-files-service
port:
number: 80
# Admin interface
- path: /admin(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: eveai-app-service
port:
number: 5001
# API endpoints
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: eveai-api-service
port:
number: 5003
# Chat client
- path: /chat-client(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: eveai-chat-client-service
port:
number: 5004
# Root redirect naar admin (exact match)
- path: /
pathType: Exact
backend:
service:
name: eveai-app-service
port:
number: 5001

480
k8s/dev/eveai-services.yaml Normal file
View File

@@ -0,0 +1,480 @@
# EveAI Application Services for Dev Environment
# File: eveai-services.yaml
---
# Shared Logs PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-logs-pvc
namespace: eveai-dev
spec:
accessModes:
- ReadWriteMany
storageClassName: local-storage
resources:
requests:
storage: 5Gi
selector:
matchLabels:
app: eveai
environment: dev
---
# EveAI App Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-app
namespace: eveai-dev
labels:
app: eveai-app
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: eveai-app
tier: frontend
template:
metadata:
labels:
app: eveai-app
tier: frontend
spec:
containers:
- name: eveai-app
image: registry.ask-eve-ai-local.com/josakola/eveai_app:latest
ports:
- containerPort: 5001
- containerPort: 8000
env:
- name: COMPONENT_NAME
value: "eveai_app"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
livenessProbe:
httpGet:
path: /
port: 5001
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 5001
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 3
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always
---
# EveAI App Service
apiVersion: v1
kind: Service
metadata:
name: eveai-app-service
namespace: eveai-dev
labels:
app: eveai-app
spec:
type: ClusterIP
ports:
- port: 5001
targetPort: 5001
protocol: TCP
selector:
app: eveai-app
---
# EveAI API Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-api
namespace: eveai-dev
labels:
app: eveai-api
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: eveai-api
tier: frontend
template:
metadata:
labels:
app: eveai-api
tier: frontend
spec:
containers:
- name: eveai-api
image: registry.ask-eve-ai-local.com/josakola/eveai_api:latest
ports:
- containerPort: 5003
- containerPort: 8000
env:
- name: COMPONENT_NAME
value: "eveai_api"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
livenessProbe:
httpGet:
path: /
port: 5003
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 5003
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 3
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always
---
# EveAI API Service
apiVersion: v1
kind: Service
metadata:
name: eveai-api-service
namespace: eveai-dev
labels:
app: eveai-api
spec:
type: ClusterIP
ports:
- port: 5003
targetPort: 5003
protocol: TCP
selector:
app: eveai-api
---
# EveAI Chat Client Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-chat-client
namespace: eveai-dev
labels:
app: eveai-chat-client
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: eveai-chat-client
tier: frontend
template:
metadata:
labels:
app: eveai-chat-client
tier: frontend
spec:
containers:
- name: eveai-chat-client
image: registry.ask-eve-ai-local.com/josakola/eveai_chat_client:latest
ports:
- containerPort: 5004
- containerPort: 8000
env:
- name: COMPONENT_NAME
value: "eveai_chat_client"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
livenessProbe:
httpGet:
path: /
port: 5004
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 5004
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 3
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always
---
# EveAI Chat Client Service
apiVersion: v1
kind: Service
metadata:
name: eveai-chat-client-service
namespace: eveai-dev
labels:
app: eveai-chat-client
spec:
type: ClusterIP
ports:
- port: 5004
targetPort: 5004
protocol: TCP
selector:
app: eveai-chat-client
---
# EveAI Workers Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-workers
namespace: eveai-dev
labels:
app: eveai-workers
environment: dev
spec:
replicas: 2 # Multiple workers for parallel processing
selector:
matchLabels:
app: eveai-workers
tier: backend
template:
metadata:
labels:
app: eveai-workers
tier: backend
spec:
containers:
- name: eveai-workers
image: registry.ask-eve-ai-local.com/josakola/eveai_workers:latest
ports:
- containerPort: 8000
env:
- name: COMPONENT_NAME
value: "eveai_workers"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always
---
# EveAI Chat Workers Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-chat-workers
namespace: eveai-dev
labels:
app: eveai-chat-workers
environment: dev
spec:
replicas: 2 # Multiple workers for parallel processing
selector:
matchLabels:
app: eveai-chat-workers
tier: backend
template:
metadata:
labels:
app: eveai-chat-workers
tier: backend
spec:
containers:
- name: eveai-chat-workers
image: registry.ask-eve-ai-local.com/josakola/eveai_chat_workers:latest
ports:
- containerPort: 8000
env:
- name: COMPONENT_NAME
value: "eveai_chat_workers"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always
---
# EveAI Beat Deployment (Celery scheduler)
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-beat
namespace: eveai-dev
labels:
app: eveai-beat
environment: dev
spec:
replicas: 1 # Only one beat scheduler needed
selector:
matchLabels:
app: eveai-beat
tier: backend
template:
metadata:
labels:
app: eveai-beat
tier: backend
spec:
containers:
- name: eveai-beat
image: registry.ask-eve-ai-local.com/josakola/eveai_beat:latest
env:
- name: COMPONENT_NAME
value: "eveai_beat"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always
---
# EveAI Entitlements Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: eveai-entitlements
namespace: eveai-dev
labels:
app: eveai-entitlements
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: eveai-entitlements
tier: backend
template:
metadata:
labels:
app: eveai-entitlements
tier: backend
spec:
containers:
- name: eveai-entitlements
image: registry.ask-eve-ai-local.com/josakola/eveai_entitlements:latest
ports:
- containerPort: 8000
env:
- name: COMPONENT_NAME
value: "eveai_entitlements"
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
volumeMounts:
- name: app-logs
mountPath: /app/logs
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-logs-pvc
restartPolicy: Always

View File

@@ -0,0 +1,19 @@
# Ingress-NGINX Controller Resource Patch
# File: ingress-nginx-resources-patch.yaml
# Purpose: Patch the ingress-nginx-controller deployment with higher resource limits
# to prevent pthread_create() failures and worker process crashes
#
# This is a strategic merge patch that will be applied using:
# kubectl patch deployment ingress-nginx-controller -n ingress-nginx --patch-file=<this-file>
spec:
template:
spec:
containers:
- name: controller
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi

View File

@@ -0,0 +1,93 @@
# Kind configuration for EveAI Dev Environment
# File: kind-dev-cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: eveai-dev-cluster
networking:
# API server configuration
apiServerAddress: "127.0.0.1"
apiServerPort: 3000
# Pod subnet (avoid conflicts with host network)
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/12"
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
# Minimal port mappings - only Ingress and essential monitoring
extraPortMappings:
# Ingress Controller - Main entry point (all app access via Ingress)
- containerPort: 80
hostPort: 3080
protocol: TCP
- containerPort: 443
hostPort: 3443
protocol: TCP
# Essential monitoring ports (optional - for direct access)
# Redis (for direct debugging if needed)
- containerPort: 30006
hostPort: 3006
protocol: TCP
# MinIO S3 API (for direct S3 access)
- containerPort: 30008
hostPort: 3008
protocol: TCP
# MinIO Console (for direct management)
- containerPort: 30009
hostPort: 3009
protocol: TCP
# Prometheus (for direct metrics access)
- containerPort: 30010
hostPort: 3010
protocol: TCP
# Grafana (for direct dashboard access)
- containerPort: 30012
hostPort: 3012
protocol: TCP
# Static files rsync access
- containerPort: 30873
hostPort: 3873
protocol: TCP
# Mount points for persistent data on host
extraMounts:
# MinIO data persistence
- hostPath: $HOME/k8s-data/dev/minio
containerPath: /mnt/minio-data
# Application logs
- hostPath: $HOME/k8s-data/dev/logs
containerPath: /mnt/app-logs
# Prometheus data
- hostPath: $HOME/k8s-data/dev/prometheus
containerPath: /mnt/prometheus-data
# Grafana data
- hostPath: $HOME/k8s-data/dev/grafana
containerPath: /mnt/grafana-data
# mkcert CA certificate
- hostPath: $HOME/k8s-data/dev/certs
containerPath: /usr/local/share/ca-certificates
# Configure registry access - temporarily disabled for testing
# containerdConfigPatches:
# - |-
# [plugins."io.containerd.grpc.v1.cri".registry]
# config_path = "/etc/containerd/certs.d"
# [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
# [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.ask-eve-ai-local.com"]
# endpoint = ["https://registry.ask-eve-ai-local.com"]
# [plugins."io.containerd.grpc.v1.cri".registry.configs]
# [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.ask-eve-ai-local.com".tls]
# ca_file = "/usr/local/share/ca-certificates/mkcert-ca.crt"
# insecure_skip_verify = false

View File

@@ -0,0 +1,19 @@
# Metrics Server Patch for Kind Compatibility
# File: metrics-server-patch.yaml
# Purpose: Patch the metrics-server deployment with Kind-specific configuration
# and appropriate resource limits for development environment
#
# This is a strategic merge patch that will be applied using:
# kubectl patch deployment metrics-server -n kube-system --patch-file=<this-file>
spec:
template:
spec:
containers:
- name: metrics-server
args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls

View File

@@ -0,0 +1,328 @@
# Flower (Celery Monitoring) Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: flower
namespace: eveai-dev
labels:
app: flower
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: flower
template:
metadata:
labels:
app: flower
spec:
containers:
- name: flower
image: registry.ask-eve-ai-local.com/josakola/flower:latest
ports:
- containerPort: 5555
envFrom:
- configMapRef:
name: eveai-config
- secretRef:
name: eveai-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "300m"
restartPolicy: Always
---
# Flower Service
apiVersion: v1
kind: Service
metadata:
name: flower-service
namespace: eveai-dev
labels:
app: flower
spec:
type: NodePort
ports:
- port: 5555
targetPort: 5555
nodePort: 30007 # Maps to host port 3007
protocol: TCP
selector:
app: flower
---
# Prometheus PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-data-pvc
namespace: eveai-dev
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 5Gi
selector:
matchLabels:
app: prometheus
environment: dev
---
# Prometheus Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: eveai-dev
labels:
app: prometheus
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: registry.ask-eve-ai-local.com/josakola/prometheus:latest
ports:
- containerPort: 9090
args:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
volumeMounts:
- name: prometheus-data
mountPath: /prometheus
livenessProbe:
httpGet:
path: /-/healthy
port: 9090
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /-/ready
port: 9090
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: prometheus-data
persistentVolumeClaim:
claimName: prometheus-data-pvc
restartPolicy: Always
---
# Prometheus Service
apiVersion: v1
kind: Service
metadata:
name: prometheus-service
namespace: eveai-dev
labels:
app: prometheus
spec:
type: NodePort
ports:
- port: 9090
targetPort: 9090
nodePort: 30010 # Maps to host port 3010
protocol: TCP
selector:
app: prometheus
---
# Pushgateway Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: pushgateway
namespace: eveai-dev
labels:
app: pushgateway
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: pushgateway
template:
metadata:
labels:
app: pushgateway
spec:
containers:
- name: pushgateway
image: prom/pushgateway:latest
ports:
- containerPort: 9091
livenessProbe:
httpGet:
path: /-/healthy
port: 9091
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /-/ready
port: 9091
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "300m"
restartPolicy: Always
---
# Pushgateway Service
apiVersion: v1
kind: Service
metadata:
name: pushgateway-service
namespace: eveai-dev
labels:
app: pushgateway
spec:
type: NodePort
ports:
- port: 9091
targetPort: 9091
nodePort: 30011 # Maps to host port 3011
protocol: TCP
selector:
app: pushgateway
---
# Grafana PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grafana-data-pvc
namespace: eveai-dev
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 1Gi
selector:
matchLabels:
app: grafana
environment: dev
---
# Grafana Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: eveai-dev
labels:
app: grafana
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: registry.ask-eve-ai-local.com/josakola/grafana:latest
ports:
- containerPort: 3000
env:
- name: GF_SECURITY_ADMIN_USER
value: "admin"
- name: GF_SECURITY_ADMIN_PASSWORD
value: "admin"
- name: GF_USERS_ALLOW_SIGN_UP
value: "false"
volumeMounts:
- name: grafana-data
mountPath: /var/lib/grafana
livenessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: grafana-data
persistentVolumeClaim:
claimName: grafana-data-pvc
restartPolicy: Always
---
# Grafana Service
apiVersion: v1
kind: Service
metadata:
name: grafana-service
namespace: eveai-dev
labels:
app: grafana
spec:
type: NodePort
ports:
- port: 3000
targetPort: 3000
nodePort: 30012 # Maps to host port 3012
protocol: TCP
selector:
app: grafana

11
k8s/dev/namespace.yaml Normal file
View File

@@ -0,0 +1,11 @@
# Namespace for EveAI Dev Environment
# File: namespace.yaml
---
# Namespace for dev environment
apiVersion: v1
kind: Namespace
metadata:
name: eveai-dev
labels:
environment: dev
app: eveai

View File

@@ -0,0 +1,147 @@
# Network Policies for EveAI Dev Environment
# File: network-policies.yaml
# Provides proper isolation and security for Kubernetes services
---
# Default deny all ingress traffic (security first approach)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: eveai-dev
labels:
app: eveai
environment: dev
spec:
podSelector: {}
policyTypes:
- Ingress
---
# Allow ingress controller to reach app services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-to-apps
namespace: eveai-dev
labels:
app: eveai
environment: dev
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 5001 # eveai-app
- protocol: TCP
port: 5003 # eveai-api
- protocol: TCP
port: 5004 # eveai-chat-client
---
# Allow app services to communicate with backend services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-apps-to-backend
namespace: eveai-dev
labels:
app: eveai
environment: dev
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 6379 # Redis
- protocol: TCP
port: 9000 # MinIO S3 API
- protocol: TCP
port: 9001 # MinIO Console
- protocol: TCP
port: 5432 # PostgreSQL
---
# Allow internal service communication within eveai-dev namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-internal-communication
namespace: eveai-dev
labels:
app: eveai
environment: dev
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: eveai-dev
---
# Allow monitoring services to scrape metrics
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring-scraping
namespace: eveai-dev
labels:
app: eveai
environment: dev
spec:
podSelector:
matchLabels:
tier: monitoring
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090 # Prometheus
- protocol: TCP
port: 9091 # Pushgateway
- protocol: TCP
port: 5555 # Flower
- protocol: TCP
port: 3000 # Grafana
---
# Allow external access to monitoring services (NodePort)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-monitoring
namespace: eveai-dev
labels:
app: eveai
environment: dev
spec:
podSelector:
matchLabels:
tier: monitoring
policyTypes:
- Ingress
ingress:
- {} # Allow all external traffic to monitoring services

View File

@@ -0,0 +1,164 @@
# Persistent Volumes for EveAI Dev Environment
# File: persistent-volumes.yaml
---
# MinIO Data Storage
apiVersion: v1
kind: PersistentVolume
metadata:
name: minio-data-pv
labels:
app: minio
environment: dev
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/minio-data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- eveai-dev-cluster-control-plane
---
# Application Logs Storage
apiVersion: v1
kind: PersistentVolume
metadata:
name: app-logs-pv
labels:
app: eveai
environment: dev
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/app-logs
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- eveai-dev-cluster-control-plane
---
# Prometheus Data Storage
apiVersion: v1
kind: PersistentVolume
metadata:
name: prometheus-data-pv
labels:
app: prometheus
environment: dev
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/prometheus-data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- eveai-dev-cluster-control-plane
---
# Grafana Data Storage
apiVersion: v1
kind: PersistentVolume
metadata:
name: grafana-data-pv
labels:
app: grafana
environment: dev
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/grafana-data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- eveai-dev-cluster-control-plane
---
# Static Files Storage
apiVersion: v1
kind: PersistentVolume
metadata:
name: static-files-pv
labels:
app: static-files
environment: dev
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/static-files
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- eveai-dev-cluster-control-plane
---
# Static Files Persistent Volume Claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: static-files-pvc
namespace: eveai-dev
spec:
accessModes:
- ReadWriteMany
storageClassName: local-storage
resources:
requests:
storage: 1Gi
selector:
matchLabels:
app: static-files
environment: dev
---
# StorageClass for local storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

View File

@@ -0,0 +1,232 @@
# Redis and MinIO Services for EveAI Dev Environment
# File: redis-minio-services.yaml
---
# Redis Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: eveai-dev
labels:
app: redis
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
tier: backend
spec:
containers:
- name: redis
image: redis:7.2.5
ports:
- containerPort: 6379
livenessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
restartPolicy: Always
---
# Redis Service
apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: eveai-dev
labels:
app: redis
spec:
type: NodePort
ports:
- port: 6379
targetPort: 6379
nodePort: 30006 # Maps to host port 3006
protocol: TCP
selector:
app: redis
---
# MinIO Persistent Volume Claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data-pvc
namespace: eveai-dev
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 10Gi
selector:
matchLabels:
app: minio
environment: dev
---
# MinIO Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
namespace: eveai-dev
labels:
app: minio
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
tier: backend
spec:
containers:
- name: minio
image: minio/minio
command:
- minio
- server
- /data
- --console-address
- ":9001"
ports:
- containerPort: 9000
name: api
- containerPort: 9001
name: console
env:
- name: MINIO_ROOT_USER
valueFrom:
configMapKeyRef:
name: eveai-config
key: MINIO_ACCESS_KEY
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: eveai-secrets
key: MINIO_SECRET_KEY
volumeMounts:
- name: minio-data
mountPath: /data
livenessProbe:
httpGet:
path: /minio/health/live
port: 9000
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /minio/health/ready
port: 9000
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 3
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "1000m"
volumes:
- name: minio-data
persistentVolumeClaim:
claimName: minio-data-pvc
restartPolicy: Always
---
# MinIO Service (API)
apiVersion: v1
kind: Service
metadata:
name: minio-service
namespace: eveai-dev
labels:
app: minio
spec:
type: NodePort
ports:
- port: 9000
targetPort: 9000
nodePort: 30008 # Maps to host port 3008
protocol: TCP
name: api
selector:
app: minio
---
# MinIO Console Service
apiVersion: v1
kind: Service
metadata:
name: minio-console-service
namespace: eveai-dev
labels:
app: minio
spec:
type: NodePort
ports:
- port: 9001
targetPort: 9001
nodePort: 30009 # Maps to host port 3009
protocol: TCP
name: console
selector:
app: minio
---
# Redis Alias Service (for application compatibility)
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: eveai-dev
labels:
app: redis
spec:
type: ClusterIP
ports:
- port: 6379
targetPort: 6379
protocol: TCP
selector:
app: redis

495
k8s/dev/setup-dev-cluster.sh Executable file
View File

@@ -0,0 +1,495 @@
#!/bin/bash
# Setup script voor EveAI Dev Kind Cluster
# File: setup-dev-cluster.sh
set -e
echo "🚀 Setting up EveAI Dev Kind Cluster..."
CLUSTER_NAME="eveai-dev-cluster"
# Colors voor output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function voor colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if required tools are installed
check_prerequisites() {
print_status "Checking prerequisites..."
if ! command -v kind &> /dev/null; then
print_error "kind is not installed. Please install kind first."
echo "Install via: go install sigs.k8s.io/kind@latest"
exit 1
fi
if ! command -v kubectl &> /dev/null; then
print_error "kubectl is not installed. Please install kubectl first."
exit 1
fi
if ! command -v podman &> /dev/null; then
print_error "podman is not installed. Please install podman first."
exit 1
fi
if ! command -v envsubst &> /dev/null; then
print_error "envsubst is not installed. Please install envsubst first"
fi
print_success "All prerequisites are installed"
}
# Create host directories for persistent volumes
create_host_directories() {
print_status "Creating host directories for persistent storage..."
BASE_DIR="$HOME/k8s-data/dev"
directories=(
"$BASE_DIR/minio"
"$BASE_DIR/logs"
"$BASE_DIR/prometheus"
"$BASE_DIR/grafana"
"$BASE_DIR/certs"
"$BASE_DIR/static-files" )
for dir in "${directories[@]}"; do
if [ ! -d "$dir" ]; then
mkdir -p "$dir"
print_status "Created directory: $dir"
else
print_status "Directory already exists: $dir"
fi
done
# Set proper permissions
# chmod -R 755 "$BASE_DIR"
print_success "Host directories created and configured"
}
# Create Kind cluster
create_cluster() {
print_status "Creating Kind cluster..."
if kind get clusters | grep -q "eveai-dev-cluster"; then
print_warning "Cluster 'eveai-dev-cluster' already exists"
echo -n "Do you want to delete and recreate it? (y/N): "
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
print_status "Deleting existing cluster..."
kind delete cluster --name eveai-dev-cluster
else
print_status "Using existing cluster"
return 0
fi
fi
KIND_CONFIG="kind-dev-cluster.yaml"
if [ ! -f "${KIND_CONFIG}" ]; then
print_error "Config '${KIND_CONFIG}' not found in $(pwd)"
exit 1
fi
print_status "Creating new Kind cluster with configuration..."
# Genereer expanded config met envsubst
EXPANDED_CONFIG="$(mktemp --suffix=.yaml)"
envsubst < "${KIND_CONFIG}" > "${EXPANDED_CONFIG}"
# Voorkeursmethode: start in user-scope met expliciete delegatie
if command -v systemd-run >/dev/null 2>&1; then
systemd-run --scope --user -p "Delegate=yes" \
env KIND_EXPERIMENTAL_PROVIDER=podman \
kind create cluster --name "${CLUSTER_NAME}" --config "${EXPANDED_CONFIG}"
else
# Fallback
print_warning "Start zonder systemd-run scope; kan mislukken bij ontbrekende delegatie."
kind create cluster --name "${CLUSTER_NAME}" --config "${EXPANDED_CONFIG}"
fi
# Cleanup temporary config
rm -f "${EXPANDED_CONFIG}"
# Wait for cluster to be ready
print_status "Waiting for cluster to be ready..."
kubectl wait --for=condition=Ready nodes --all --timeout=300s
# Update CA certificates in Kind node
if command -v podman &> /dev/null; then
podman exec eveai-dev-cluster-control-plane update-ca-certificates
podman exec eveai-dev-cluster-control-plane systemctl restart containerd
else
docker exec eveai-dev-cluster-control-plane update-ca-certificates
docker exec eveai-dev-cluster-control-plane systemctl restart containerd
fi
print_success "Kind cluster created successfully"
}
# Verify CRI status and functionality
verify_cri_status() {
print_status "Verifying CRI status..."
# Wait for services to stabilize
sleep 15
# Test CRI connectivity
if podman exec "${CLUSTER_NAME}-control-plane" crictl version &>/dev/null; then
print_success "CRI is functional"
# Show CRI version info
print_status "CRI version information:"
podman exec "${CLUSTER_NAME}-control-plane" crictl version
else
print_error "CRI is not responding - checking containerd logs"
podman exec "${CLUSTER_NAME}-control-plane" journalctl -u containerd --no-pager -n 20
print_error "Checking kubelet logs"
podman exec "${CLUSTER_NAME}-control-plane" journalctl -u kubelet --no-pager -n 10
return 1
fi
# Verify node readiness
print_status "Waiting for node to become Ready..."
local max_attempts=30
local attempt=0
while [ $attempt -lt $max_attempts ]; do
if kubectl get nodes | grep -q "Ready"; then
print_success "Node is Ready"
return 0
fi
attempt=$((attempt + 1))
print_status "Attempt $attempt/$max_attempts - waiting for node readiness..."
sleep 10
done
print_error "Node failed to become Ready within timeout"
kubectl get nodes -o wide
return 1
}
# Install Ingress Controller
install_ingress_controller() {
print_status "Installing NGINX Ingress Controller..."
# Install NGINX Ingress Controller for Kind
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/kind/deploy.yaml
# Wait for Ingress Controller to be ready
print_status "Waiting for Ingress Controller to be ready..."
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=300s
if [ $? -eq 0 ]; then
print_success "NGINX Ingress Controller installed and ready"
else
print_warning "Ingress Controller not ready, trying to label node..."
# Label the node for ingress (fallback for scheduling issues)
kubectl label node eveai-dev-cluster-control-plane ingress-ready=true --overwrite
# Wait again for Ingress Controller to be ready
print_status "Waiting for Ingress Controller after node labeling..."
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=300s
if [ $? -eq 0 ]; then
print_success "NGINX Ingress Controller ready after node labeling"
else
print_error "Failed to install or start Ingress Controller even after node labeling"
exit 1
fi
fi
# Verify Ingress Controller status
print_status "Ingress Controller status:"
kubectl get pods -n ingress-nginx
kubectl get services -n ingress-nginx
}
# Patch Ingress Controller Resources
patch_ingress_resources() {
print_status "Patching Ingress Controller resources..."
# Wait a moment for the deployment to be fully created
sleep 5
# Check if patch file exists
local patch_file="ingress-nginx-resources-patch.yaml"
if [[ ! -f "$patch_file" ]]; then
print_error "Patch file not found: $patch_file"
return 1
fi
# Patch the ingress-nginx-controller deployment with higher resource limits
print_status "Updating resource limits for ingress-nginx-controller using manifest file..."
# Apply patch with retry logic
local max_attempts=5
local attempt=1
local success=false
while [ $attempt -le $max_attempts ] && [ "$success" = false ]; do
print_status "Attempt $attempt/$max_attempts - patching ingress controller resources..."
if kubectl patch deployment ingress-nginx-controller -n ingress-nginx --patch-file "$patch_file"; then
print_success "Successfully patched ingress-nginx-controller resources"
success=true
else
if [ $attempt -lt $max_attempts ]; then
print_warning "Patch attempt $attempt failed, retrying in 5 seconds..."
sleep 5
attempt=$((attempt + 1))
else
print_error "Failed to patch ingress-nginx-controller resources after $max_attempts attempts"
return 1
fi
fi
done
# Wait for rollout to complete
print_status "Waiting for ingress controller rollout to complete..."
kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx --timeout=300s
if [ $? -eq 0 ]; then
print_success "Ingress Controller resource patch completed successfully"
# Verify the new resource settings
print_status "Verifying new resource settings..."
kubectl describe deployment ingress-nginx-controller -n ingress-nginx | grep -A 10 "Limits:\|Requests:" || true
else
print_error "Ingress Controller rollout failed"
return 1
fi
}
# Install Metrics Server
install_metrics_server() {
print_status "Installing Metrics Server..."
# Apply metrics server with Kind-specific configuration
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Check if patch file exists
local patch_file="metrics-server-patch.yaml"
if [[ ! -f "$patch_file" ]]; then
print_error "Patch file not found: $patch_file"
return 1
fi
# Patch metrics server for Kind (disable TLS verification)
print_status "Patching Metrics Server for Kind compatibility using manifest file..."
# Wait for metrics server deployment to exist
local max_wait=30
local wait_count=0
while ! kubectl get deployment metrics-server -n kube-system &> /dev/null; do
if [ $wait_count -ge $max_wait ]; then
print_error "Metrics server deployment not found after waiting"
return 1
fi
sleep 2
wait_count=$((wait_count + 1))
done
# Apply the patch
if kubectl patch deployment metrics-server -n kube-system --patch-file "$patch_file"; then
print_success "Successfully patched metrics-server configuration"
else
print_warning "Failed to patch metrics-server, but continuing..."
fi
# Wait for metrics server to be ready
print_status "Waiting for Metrics Server to be ready..."
kubectl wait --for=condition=available deployment/metrics-server -n kube-system --timeout=300s
if [ $? -eq 0 ]; then
print_success "Metrics Server installed and ready"
# Test metrics server
print_status "Testing metrics server..."
sleep 10 # Give metrics server time to collect initial metrics
if kubectl top nodes &> /dev/null; then
print_success "Metrics Server is working correctly"
else
print_warning "Metrics Server installed but may need more time to collect metrics"
fi
else
print_warning "Metrics Server installation completed but readiness check failed"
fi
}
# Apply Kubernetes manifests
apply_manifests() {
print_status "Applying Kubernetes manifests..."
# Apply base manifests in correct order (namespace.yaml handles namespace creation)
manifests=(
"namespace.yaml"
"persistent-volumes.yaml"
"static-files-access.yaml"
"config-secrets.yaml"
"network-policies.yaml"
)
for manifest in "${manifests[@]}"; do
if [ -f "$manifest" ]; then
print_status "Applying $manifest..."
# Apply with retry logic for race condition handling
local max_attempts=3
local attempt=1
local success=false
while [ $attempt -le $max_attempts ] && [ "$success" = false ]; do
if kubectl apply -f "$manifest"; then
print_success "Successfully applied: $manifest"
success=true
else
if [ $attempt -lt $max_attempts ]; then
print_warning "Attempt $attempt failed for $manifest, retrying in 3 seconds..."
sleep 3
attempt=$((attempt + 1))
else
print_error "Failed to apply $manifest after $max_attempts attempts"
return 1
fi
fi
done
else
print_warning "Manifest $manifest not found, skipping..."
fi
done
print_success "Base manifests applied successfully"
}
# Configure registry certificates and containerd
configure_registry_certificates() {
print_status "Configuring registry certificates and containerd..."
# Update CA certificates in the cluster
print_status "Updating CA certificates..."
kubectl debug node/eveai-dev-cluster-control-plane -it --image=busybox -- sh -c "
chroot /host update-ca-certificates 2>/dev/null || true
" 2>/dev/null || print_warning "Certificate update may have failed"
# Create containerd registry configuration directory
print_status "Creating containerd registry configuration..."
kubectl debug node/eveai-dev-cluster-control-plane -it --image=busybox -- sh -c "
chroot /host mkdir -p /etc/containerd/certs.d/registry.ask-eve-ai-local.com
" 2>/dev/null || print_warning "Failed to create containerd config directory"
# Configure registry hosts.toml
print_status "Configuring registry hosts.toml..."
kubectl debug node/eveai-dev-cluster-control-plane -it --image=busybox -- sh -c "
chroot /host sh -c 'cat > /etc/containerd/certs.d/registry.ask-eve-ai-local.com/hosts.toml << EOF
server = \"https://registry.ask-eve-ai-local.com\"
[host.\"https://registry.ask-eve-ai-local.com\"]
capabilities = [\"pull\", \"resolve\"]
ca = [\"/usr/local/share/ca-certificates/mkcert-ca.crt\"]
EOF'
" 2>/dev/null || print_warning "Failed to create hosts.toml"
# Restart containerd to apply configuration
print_status "Restarting containerd..."
kubectl debug node/eveai-dev-cluster-control-plane -it --image=busybox -- sh -c "
chroot /host systemctl restart containerd
" 2>/dev/null || print_warning "Failed to restart containerd"
print_success "Registry certificates and containerd configured"
}
# Verify cluster status
verify_cluster() {
print_status "Verifying cluster status..."
# Check nodes
print_status "Cluster nodes:"
kubectl get nodes
# Check namespaces
print_status "Namespaces:"
kubectl get namespaces
# Check persistent volumes
print_status "Persistent volumes:"
kubectl get pv
# Check if registry is accessible from cluster
print_status "Testing registry connectivity..."
if kubectl run test-registry --image=registry.ask-eve-ai-local.com/josakola/nginx:latest --dry-run=server &> /dev/null; then
print_success "Registry is accessible from cluster"
kubectl delete pod test-registry --ignore-not-found=true &> /dev/null || true
else
print_warning "Registry connectivity test failed - this might be expected if images aren't pushed yet"
fi
}
# Main execution
main() {
echo "=================================================="
echo "🏗️ EveAI Dev Kind Cluster Setup"
echo "=================================================="
check_prerequisites
create_host_directories
create_cluster
verify_cri_status
install_ingress_controller
patch_ingress_resources
install_metrics_server
apply_manifests
configure_registry_certificates
verify_cluster
echo ""
echo "=================================================="
print_success "EveAI Dev Kind Cluster setup completed!"
echo "=================================================="
echo ""
echo "📋 Next steps:"
echo "1. Deploy your application services using: ./deploy-all-services.sh"
echo "2. Access services via Ingress: http://minty.ask-eve-ai-local.com:3080"
echo ""
echo "🔧 Useful commands:"
echo " kubectl config current-context # Verify you're using the right cluster"
echo " kubectl get all -n eveai-dev # Check all resources in dev namespace"
echo " kubectl get ingress -n eveai-dev # Check Ingress resources"
echo " kind delete cluster --name eveai-dev-cluster # Delete cluster when done"
echo ""
echo "📊 Service Access (via Ingress):"
echo " - Main App: http://minty.ask-eve-ai-local.com:3080/admin/"
echo " - API: http://minty.ask-eve-ai-local.com:3080/api/"
echo " - Chat Client: http://minty.ask-eve-ai-local.com:3080/chat-client/"
echo " - Static Files: http://minty.ask-eve-ai-local.com:3080/static/"
}
# Run main function
main "$@"

View File

@@ -0,0 +1,106 @@
# Static Files Access Pod for EveAI Dev Environment
# File: static-files-access.yaml
# Provides rsync daemon access to static files PVC
---
# Rsync Access Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: static-files-access
namespace: eveai-dev
labels:
app: static-files-access
environment: dev
spec:
replicas: 1
selector:
matchLabels:
app: static-files-access
template:
metadata:
labels:
app: static-files-access
spec:
containers:
- name: rsync-daemon
image: alpine:latest
command: ["/bin/sh"]
args:
- -c
- |
# Install rsync
apk add --no-cache rsync
# Create rsync configuration
cat > /etc/rsyncd.conf << 'RSYNC_EOF'
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log
port = 873
[static]
path = /data/static
comment = Static Files Volume
uid = nobody
gid = nobody
read only = false
list = yes
auth users =
secrets file =
hosts allow = *
RSYNC_EOF
# Create target directory
mkdir -p /data/static
chown nobody:nobody /data/static
# Start rsync daemon
echo "Starting rsync daemon..."
rsync --daemon --no-detach --config=/etc/rsyncd.conf
ports:
- containerPort: 873
name: rsync
volumeMounts:
- name: static-files
mountPath: /data
livenessProbe:
tcpSocket:
port: 873
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
tcpSocket:
port: 873
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
memory: "32Mi"
cpu: "25m"
limits:
memory: "64Mi"
cpu: "50m"
volumes:
- name: static-files
persistentVolumeClaim:
claimName: static-files-pvc
---
# NodePort Service for external rsync access
apiVersion: v1
kind: Service
metadata:
name: static-files-access-service
namespace: eveai-dev
labels:
app: static-files-access
spec:
type: NodePort
ports:
- port: 873
targetPort: 873
nodePort: 30873
protocol: TCP
name: rsync
selector:
app: static-files-access

View File

@@ -0,0 +1,122 @@
# Static Files Service for EveAI Dev Environment (v2 - PersistentVolume based)
# File: static-files-service.yaml
---
# Static Files ConfigMap (enhanced caching)
apiVersion: v1
kind: ConfigMap
metadata:
name: static-files-config
namespace: eveai-dev
data:
nginx.conf: |
server {
listen 80;
server_name _;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/css application/javascript application/json image/svg+xml;
location /static/ {
alias /usr/share/nginx/html/static/;
# Aggressive caching voor versioned assets
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options nosniff;
}
# Moderate caching voor images
location ~* \.(png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public";
}
# Default caching
expires 1h;
add_header Cache-Control "public";
}
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}
}
---
# Static Files Deployment (GEEN CUSTOM IMAGE!)
apiVersion: apps/v1
kind: Deployment
metadata:
name: static-files
namespace: eveai-dev
labels:
app: static-files
environment: dev
spec:
replicas: 2 # Voor high availability
selector:
matchLabels:
app: static-files
template:
metadata:
labels:
app: static-files
spec:
containers:
- name: nginx
image: nginx:alpine # 🎉 STANDARD IMAGE!
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
- name: static-files
mountPath: /usr/share/nginx/html
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
volumes:
- name: nginx-config
configMap:
name: static-files-config
- name: static-files
persistentVolumeClaim:
claimName: static-files-pvc
---
# Service (ongewijzigd)
apiVersion: v1
kind: Service
metadata:
name: static-files-service
namespace: eveai-dev
labels:
app: static-files
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: static-files

517
k8s/k8s_env_switch.sh Normal file
View File

@@ -0,0 +1,517 @@
#!/usr/bin/env zsh
# Function to display usage information
usage() {
echo "Usage: source $0 <environment> [version]"
echo " environment: The environment to use (dev, test, bugfix, integration, prod)"
echo " version : (Optional) Specific release version to deploy"
echo " If not specified, uses 'latest' (except for dev environment)"
}
# Check if the script is sourced - improved for both bash and zsh
is_sourced() {
if [[ -n "$ZSH_VERSION" ]]; then
# In zsh, check if we're in a sourced context
[[ "$ZSH_EVAL_CONTEXT" =~ "(:file|:cmdsubst)" ]] || [[ "$0" != "$ZSH_ARGZERO" ]]
else
# In bash, compare BASH_SOURCE with $0
[[ "${BASH_SOURCE[0]}" != "${0}" ]]
fi
}
if ! is_sourced; then
echo "Error: This script must be sourced, not executed directly."
echo "Please run: source $0 <environment> [version]"
if [[ -n "$ZSH_VERSION" ]]; then
return 1 2>/dev/null || exit 1
else
exit 1
fi
fi
# Check if an environment is provided
if [ $# -eq 0 ]; then
usage
return 1
fi
ENVIRONMENT=$1
VERSION=${2:-latest} # Default to latest if not specified
# Check if required tools are available
if ! command -v kubectl &> /dev/null; then
echo "Error: kubectl is not installed or not in PATH"
echo "Please install kubectl first"
return 1
fi
if ! command -v kind &> /dev/null; then
echo "Error: kind is not installed or not in PATH"
echo "Please install kind first"
return 1
fi
echo "Using kubectl: $(command -v kubectl)"
echo "Using kind: $(command -v kind)"
# Set variables based on the environment
case $ENVIRONMENT in
dev)
K8S_CLUSTER="kind-eveai-dev-cluster"
K8S_NAMESPACE="eveai-dev"
K8S_CONFIG_DIR="$PWD/k8s/dev"
VERSION="latest" # Always use latest for dev
;;
test)
K8S_CLUSTER="kind-eveai-test-cluster"
K8S_NAMESPACE="eveai-test"
K8S_CONFIG_DIR="$PWD/k8s/test"
;;
bugfix)
K8S_CLUSTER="kind-eveai-bugfix-cluster"
K8S_NAMESPACE="eveai-bugfix"
K8S_CONFIG_DIR="$PWD/k8s/bugfix"
;;
integration)
K8S_CLUSTER="kind-eveai-integration-cluster"
K8S_NAMESPACE="eveai-integration"
K8S_CONFIG_DIR="$PWD/k8s/integration"
;;
prod)
K8S_CLUSTER="kind-eveai-prod-cluster"
K8S_NAMESPACE="eveai-prod"
K8S_CONFIG_DIR="$PWD/k8s/prod"
;;
*)
echo "Invalid environment: $ENVIRONMENT"
usage
return 1
;;
esac
# Set up logging directories
LOG_DIR="$HOME/k8s-logs/$ENVIRONMENT"
mkdir -p "$LOG_DIR"
# Check if config directory exists
if [[ ! -d "$K8S_CONFIG_DIR" ]]; then
echo "Warning: Config directory '$K8S_CONFIG_DIR' does not exist."
if [[ "$ENVIRONMENT" != "dev" && -d "$PWD/k8s/dev" ]]; then
echo -n "Do you want to create it based on dev environment? (y/n): "
read -r CREATE_DIR
if [[ "$CREATE_DIR" == "y" || "$CREATE_DIR" == "Y" ]]; then
mkdir -p "$K8S_CONFIG_DIR"
cp -r "$PWD/k8s/dev/"* "$K8S_CONFIG_DIR/"
echo "Created $K8S_CONFIG_DIR with dev environment templates."
echo "Please review and modify the configurations for $ENVIRONMENT environment."
else
echo "Cannot proceed without a valid config directory."
return 1
fi
else
echo "Cannot create $K8S_CONFIG_DIR: dev environment not found."
return 1
fi
fi
# Set cluster context
echo "Setting kubectl context to $K8S_CLUSTER..."
if kubectl config use-context "$K8S_CLUSTER" &>/dev/null; then
echo "✅ Using cluster context: $K8S_CLUSTER"
else
echo "⚠️ Warning: Failed to switch to context $K8S_CLUSTER"
echo " Make sure the cluster is running: kind get clusters"
fi
# Set environment variables
export K8S_ENVIRONMENT=$ENVIRONMENT
export K8S_VERSION=$VERSION
export K8S_CLUSTER=$K8S_CLUSTER
export K8S_NAMESPACE=$K8S_NAMESPACE
export K8S_CONFIG_DIR=$K8S_CONFIG_DIR
export K8S_LOG_DIR=$LOG_DIR
echo "Set K8S_ENVIRONMENT to $ENVIRONMENT"
echo "Set K8S_VERSION to $VERSION"
echo "Set K8S_CLUSTER to $K8S_CLUSTER"
echo "Set K8S_NAMESPACE to $K8S_NAMESPACE"
echo "Set K8S_CONFIG_DIR to $K8S_CONFIG_DIR"
echo "Set K8S_LOG_DIR to $LOG_DIR"
# Source supporting scripts
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]:-$0}")"
if [[ -f "$SCRIPT_DIR/scripts/k8s-functions.sh" ]]; then
source "$SCRIPT_DIR/scripts/k8s-functions.sh"
else
echo "Warning: k8s-functions.sh not found, some functions may not work"
fi
if [[ -f "$SCRIPT_DIR/scripts/service-groups.sh" ]]; then
source "$SCRIPT_DIR/scripts/service-groups.sh"
else
echo "Warning: service-groups.sh not found, service groups may not be defined"
fi
if [[ -f "$SCRIPT_DIR/scripts/dependency-checks.sh" ]]; then
source "$SCRIPT_DIR/scripts/dependency-checks.sh"
else
echo "Warning: dependency-checks.sh not found, dependency checking disabled"
fi
if [[ -f "$SCRIPT_DIR/scripts/logging-utils.sh" ]]; then
source "$SCRIPT_DIR/scripts/logging-utils.sh"
else
echo "Warning: logging-utils.sh not found, logging may be limited"
fi
# Core service management functions (similar to pc* functions)
kup() {
local group=${1:-all}
log_operation "INFO" "Starting service group: $group"
deploy_service_group "$group"
}
kdown() {
local group=${1:-all}
log_operation "INFO" "Stopping service group: $group (keeping data)"
stop_service_group "$group" --keep-data
}
kstop() {
local group=${1:-all}
log_operation "INFO" "Stopping service group: $group (without removal)"
stop_service_group "$group" --stop-only
}
kstart() {
local group=${1:-all}
log_operation "INFO" "Starting stopped service group: $group"
start_service_group "$group"
}
kps() {
echo "🔍 Service Status Overview for $K8S_ENVIRONMENT:"
echo "=================================================="
kubectl get pods,services,ingress -n "$K8S_NAMESPACE" 2>/dev/null || echo "Namespace $K8S_NAMESPACE not found or no resources"
}
klogs() {
local service=$1
if [[ -z "$service" ]]; then
echo "Available services in $K8S_ENVIRONMENT:"
kubectl get deployments -n "$K8S_NAMESPACE" --no-headers 2>/dev/null | awk '{print " " $1}' || echo " No deployments found"
return 1
fi
log_operation "INFO" "Viewing logs for service: $service"
kubectl logs -f deployment/$service -n "$K8S_NAMESPACE"
}
krefresh() {
local group=${1:-all}
log_operation "INFO" "Refreshing service group: $group"
stop_service_group "$group" --stop-only
sleep 5
deploy_service_group "$group"
}
# Structured deployment of all services (like deploy-all-services.sh)
kup-all-structured() {
log_operation "INFO" "Starting structured deployment of all services"
deploy_all_structured
}
# Test connectivity to all services
ktest() {
log_operation "INFO" "Testing service connectivity"
test_connectivity_ingress
}
# Show connection information
kinfo() {
log_operation "INFO" "Showing connection information"
show_connection_info
}
# Individual service management functions for apps group
kup-app() {
log_operation "INFO" "Starting eveai-app"
check_infrastructure_ready
deploy_individual_service "eveai-app" "apps"
}
kdown-app() {
log_operation "INFO" "Stopping eveai-app"
stop_individual_service "eveai-app" --keep-data
}
kstop-app() {
log_operation "INFO" "Stopping eveai-app (without removal)"
stop_individual_service "eveai-app" --stop-only
}
kstart-app() {
log_operation "INFO" "Starting stopped eveai-app"
start_individual_service "eveai-app"
}
kup-api() {
log_operation "INFO" "Starting eveai-api"
check_infrastructure_ready
deploy_individual_service "eveai-api" "apps"
}
kdown-api() {
log_operation "INFO" "Stopping eveai-api"
stop_individual_service "eveai-api" --keep-data
}
kstop-api() {
log_operation "INFO" "Stopping eveai-api (without removal)"
stop_individual_service "eveai-api" --stop-only
}
kstart-api() {
log_operation "INFO" "Starting stopped eveai-api"
start_individual_service "eveai-api"
}
kup-chat-client() {
log_operation "INFO" "Starting eveai-chat-client"
check_infrastructure_ready
deploy_individual_service "eveai-chat-client" "apps"
}
kdown-chat-client() {
log_operation "INFO" "Stopping eveai-chat-client"
stop_individual_service "eveai-chat-client" --keep-data
}
kstop-chat-client() {
log_operation "INFO" "Stopping eveai-chat-client (without removal)"
stop_individual_service "eveai-chat-client" --stop-only
}
kstart-chat-client() {
log_operation "INFO" "Starting stopped eveai-chat-client"
start_individual_service "eveai-chat-client"
}
kup-workers() {
log_operation "INFO" "Starting eveai-workers"
check_app_dependencies "eveai-workers"
deploy_individual_service "eveai-workers" "apps"
}
kdown-workers() {
log_operation "INFO" "Stopping eveai-workers"
stop_individual_service "eveai-workers" --keep-data
}
kstop-workers() {
log_operation "INFO" "Stopping eveai-workers (without removal)"
stop_individual_service "eveai-workers" --stop-only
}
kstart-workers() {
log_operation "INFO" "Starting stopped eveai-workers"
start_individual_service "eveai-workers"
}
kup-chat-workers() {
log_operation "INFO" "Starting eveai-chat-workers"
check_app_dependencies "eveai-chat-workers"
deploy_individual_service "eveai-chat-workers" "apps"
}
kdown-chat-workers() {
log_operation "INFO" "Stopping eveai-chat-workers"
stop_individual_service "eveai-chat-workers" --keep-data
}
kstop-chat-workers() {
log_operation "INFO" "Stopping eveai-chat-workers (without removal)"
stop_individual_service "eveai-chat-workers" --stop-only
}
kstart-chat-workers() {
log_operation "INFO" "Starting stopped eveai-chat-workers"
start_individual_service "eveai-chat-workers"
}
kup-beat() {
log_operation "INFO" "Starting eveai-beat"
check_app_dependencies "eveai-beat"
deploy_individual_service "eveai-beat" "apps"
}
kdown-beat() {
log_operation "INFO" "Stopping eveai-beat"
stop_individual_service "eveai-beat" --keep-data
}
kstop-beat() {
log_operation "INFO" "Stopping eveai-beat (without removal)"
stop_individual_service "eveai-beat" --stop-only
}
kstart-beat() {
log_operation "INFO" "Starting stopped eveai-beat"
start_individual_service "eveai-beat"
}
kup-entitlements() {
log_operation "INFO" "Starting eveai-entitlements"
check_infrastructure_ready
deploy_individual_service "eveai-entitlements" "apps"
}
kdown-entitlements() {
log_operation "INFO" "Stopping eveai-entitlements"
stop_individual_service "eveai-entitlements" --keep-data
}
kstop-entitlements() {
log_operation "INFO" "Stopping eveai-entitlements (without removal)"
stop_individual_service "eveai-entitlements" --stop-only
}
kstart-entitlements() {
log_operation "INFO" "Starting stopped eveai-entitlements"
start_individual_service "eveai-entitlements"
}
# Static files management functions
kdeploy-static() {
local dry_run=""
if [[ "$1" == "--dry-run" ]]; then
dry_run="--dry-run"
fi
echo "🚀 Deploying static files to $K8S_ENVIRONMENT environment..."
"$K8S_CONFIG_DIR/../deploy-static-files.sh" "$K8S_ENVIRONMENT" "$dry_run"
}
kstatic-status() {
echo "📊 Static Files Status for $K8S_ENVIRONMENT:"
echo "============================================="
kubectl get pvc static-files-pvc -n "$K8S_NAMESPACE" 2>/dev/null || echo "PVC not found"
kubectl get pods -l app=static-files -n "$K8S_NAMESPACE" 2>/dev/null || echo "No static-files pods found"
echo ""
echo "💾 PVC Usage:"
kubectl describe pvc static-files-pvc -n "$K8S_NAMESPACE" 2>/dev/null | grep -E "(Capacity|Used)" || echo "Usage info not available"
}
# Cluster management functions
cluster-start() {
log_operation "INFO" "Starting cluster: $K8S_CLUSTER"
if kind get clusters | grep -q "${K8S_CLUSTER#kind-}"; then
echo "✅ Cluster $K8S_CLUSTER is already running"
else
echo "❌ Cluster $K8S_CLUSTER is not running"
echo "Use setup script to create cluster: $K8S_CONFIG_DIR/setup-${ENVIRONMENT}-cluster.sh"
fi
}
cluster-stop() {
log_operation "INFO" "Stopping cluster: $K8S_CLUSTER"
echo "⚠️ Note: Kind clusters cannot be stopped, only deleted"
echo "Use 'cluster-delete' to remove the cluster completely"
}
cluster-delete() {
log_operation "INFO" "Deleting cluster: $K8S_CLUSTER"
echo -n "Are you sure you want to delete cluster $K8S_CLUSTER? (y/n): "
read -r CONFIRM
if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
kind delete cluster --name "${K8S_CLUSTER#kind-}"
echo "✅ Cluster $K8S_CLUSTER deleted"
else
echo "❌ Cluster deletion cancelled"
fi
}
cluster-status() {
echo "🔍 Cluster Status for $K8S_ENVIRONMENT:"
echo "======================================"
echo "Cluster: $K8S_CLUSTER"
echo "Namespace: $K8S_NAMESPACE"
echo ""
if kind get clusters | grep -q "${K8S_CLUSTER#kind-}"; then
echo "✅ Cluster is running"
echo ""
echo "Nodes:"
kubectl get nodes 2>/dev/null || echo " Unable to get nodes"
echo ""
echo "Namespaces:"
kubectl get namespaces 2>/dev/null || echo " Unable to get namespaces"
else
echo "❌ Cluster is not running"
fi
}
# Export functions - handle both bash and zsh
if [[ -n "$ZSH_VERSION" ]]; then
# In zsh, functions are automatically available in subshells
# But we can make them available globally with typeset
typeset -f kup kdown kstop kstart kps klogs krefresh > /dev/null
typeset -f kup-all-structured ktest kinfo > /dev/null
typeset -f kup-app kdown-app kstop-app kstart-app > /dev/null
typeset -f kup-api kdown-api kstop-api kstart-api > /dev/null
typeset -f kup-chat-client kdown-chat-client kstop-chat-client kstart-chat-client > /dev/null
typeset -f kup-workers kdown-workers kstop-workers kstart-workers > /dev/null
typeset -f kup-chat-workers kdown-chat-workers kstop-chat-workers kstart-chat-workers > /dev/null
typeset -f kup-beat kdown-beat kstop-beat kstart-beat > /dev/null
typeset -f kup-entitlements kdown-entitlements kstop-entitlements kstart-entitlements > /dev/null
typeset -f cluster-start cluster-stop cluster-delete cluster-status > /dev/null
else
# Bash style export
export -f kup kdown kstop kstart kps klogs krefresh
export -f kup-all-structured ktest kinfo
export -f kup-app kdown-app kstop-app kstart-app
export -f kup-api kdown-api kstop-api kstart-api
export -f kup-chat-client kdown-chat-client kstop-chat-client kstart-chat-client
export -f kup-workers kdown-workers kstop-workers kstart-workers
export -f kup-chat-workers kdown-chat-workers kstop-chat-workers kstart-chat-workers
export -f kup-beat kdown-beat kstop-beat kstart-beat
export -f kup-entitlements kdown-entitlements kstop-entitlements kstart-entitlements
export -f cluster-start cluster-stop cluster-delete cluster-status
fi
echo "✅ Kubernetes environment switched to $ENVIRONMENT with version $VERSION"
echo "🏗️ Cluster: $K8S_CLUSTER"
echo "📁 Config Dir: $K8S_CONFIG_DIR"
echo "📝 Log Dir: $LOG_DIR"
echo ""
echo "Available commands:"
echo " Service Groups:"
echo " kup [group] - start service group (infrastructure|apps|static|monitoring|all)"
echo " kdown [group] - stop service group, keep data"
echo " kstop [group] - stop service group without removal"
echo " kstart [group] - start stopped service group"
echo " krefresh [group] - restart service group"
echo ""
echo " Structured Deployment:"
echo " kup-all-structured - deploy all services in structured order (like deploy-all-services.sh)"
echo ""
echo " Individual App Services:"
echo " kup-app - start eveai-app"
echo " kup-api - start eveai-api"
echo " kup-chat-client - start eveai-chat-client"
echo " kup-workers - start eveai-workers"
echo " kup-chat-workers - start eveai-chat-workers"
echo " kup-beat - start eveai-beat"
echo " kup-entitlements - start eveai-entitlements"
echo " (and corresponding kdown-, kstop-, kstart- functions)"
echo ""
echo " Status & Testing:"
echo " kps - show service status"
echo " klogs [service] - view service logs"
echo " ktest - test service connectivity"
echo " kinfo - show connection information"
echo ""
echo " Cluster Management:"
echo " cluster-start - start cluster"
echo " cluster-stop - stop cluster"
echo " cluster-delete - delete cluster"
echo " cluster-status - show cluster status"

View File

@@ -0,0 +1,309 @@
#!/bin/bash
# Kubernetes Dependency Checking
# File: dependency-checks.sh
# Check if a service is ready
check_service_ready() {
local service=$1
local namespace=${2:-$K8S_NAMESPACE}
local timeout=${3:-60}
log_operation "INFO" "Checking if service '$service' is ready in namespace '$namespace'"
# Check if deployment exists
if ! kubectl get deployment "$service" -n "$namespace" &>/dev/null; then
log_dependency_check "$service" "NOT_FOUND" "Deployment does not exist"
return 1
fi
# Check if deployment is ready
local ready_replicas
ready_replicas=$(kubectl get deployment "$service" -n "$namespace" -o jsonpath='{.status.readyReplicas}' 2>/dev/null)
local desired_replicas
desired_replicas=$(kubectl get deployment "$service" -n "$namespace" -o jsonpath='{.spec.replicas}' 2>/dev/null)
if [[ -z "$ready_replicas" ]]; then
ready_replicas=0
fi
if [[ -z "$desired_replicas" ]]; then
desired_replicas=1
fi
if [[ "$ready_replicas" -eq "$desired_replicas" && "$ready_replicas" -gt 0 ]]; then
log_dependency_check "$service" "READY" "All $ready_replicas/$desired_replicas replicas are ready"
return 0
else
log_dependency_check "$service" "NOT_READY" "Only $ready_replicas/$desired_replicas replicas are ready"
return 1
fi
}
# Wait for a service to become ready
wait_for_service_ready() {
local service=$1
local namespace=${2:-$K8S_NAMESPACE}
local timeout=${3:-300}
local check_interval=${4:-10}
log_operation "INFO" "Waiting for service '$service' to become ready (timeout: ${timeout}s)"
local elapsed=0
while [[ $elapsed -lt $timeout ]]; do
if check_service_ready "$service" "$namespace" 0; then
log_operation "SUCCESS" "Service '$service' is ready after ${elapsed}s"
return 0
fi
log_operation "DEBUG" "Service '$service' not ready yet, waiting ${check_interval}s... (${elapsed}/${timeout}s)"
sleep "$check_interval"
elapsed=$((elapsed + check_interval))
done
log_operation "ERROR" "Service '$service' failed to become ready within ${timeout}s"
return 1
}
# Check if infrastructure services are ready
check_infrastructure_ready() {
log_operation "INFO" "Checking infrastructure readiness"
local infrastructure_services
infrastructure_services=$(get_services_in_group "infrastructure")
if [[ $? -ne 0 ]]; then
log_operation "ERROR" "Failed to get infrastructure services"
return 1
fi
local all_ready=true
for service in $infrastructure_services; do
if ! check_service_ready "$service" "$K8S_NAMESPACE" 0; then
all_ready=false
log_operation "WARNING" "Infrastructure service '$service' is not ready"
fi
done
if [[ "$all_ready" == "true" ]]; then
log_operation "SUCCESS" "All infrastructure services are ready"
return 0
else
log_operation "ERROR" "Some infrastructure services are not ready"
log_operation "INFO" "You may need to start infrastructure first: kup infrastructure"
return 1
fi
}
# Check app-specific dependencies
check_app_dependencies() {
local service=$1
log_operation "INFO" "Checking dependencies for service '$service'"
case "$service" in
"eveai-workers"|"eveai-chat-workers")
# Workers need API to be running
if ! check_service_ready "eveai-api" "$K8S_NAMESPACE" 0; then
log_operation "ERROR" "Service '$service' requires eveai-api to be running"
log_operation "INFO" "Start API first: kup-api"
return 1
fi
;;
"eveai-beat")
# Beat needs Redis to be running
if ! check_service_ready "redis" "$K8S_NAMESPACE" 0; then
log_operation "ERROR" "Service '$service' requires redis to be running"
log_operation "INFO" "Start infrastructure first: kup infrastructure"
return 1
fi
;;
"eveai-app"|"eveai-api"|"eveai-chat-client"|"eveai-entitlements")
# Core apps need infrastructure
if ! check_infrastructure_ready; then
log_operation "ERROR" "Service '$service' requires infrastructure to be running"
return 1
fi
;;
*)
log_operation "DEBUG" "No specific dependencies defined for service '$service'"
;;
esac
log_operation "SUCCESS" "All dependencies satisfied for service '$service'"
return 0
}
# Check if a pod is running and ready
check_pod_ready() {
local pod_selector=$1
local namespace=${2:-$K8S_NAMESPACE}
local pods
pods=$(kubectl get pods -l "$pod_selector" -n "$namespace" --no-headers 2>/dev/null)
if [[ -z "$pods" ]]; then
return 1
fi
# Check if any pod is in Running state and Ready
while IFS= read -r line; do
local status=$(echo "$line" | awk '{print $3}')
local ready=$(echo "$line" | awk '{print $2}')
if [[ "$status" == "Running" && "$ready" =~ ^[1-9]/[1-9] ]]; then
# Extract ready count and total count
local ready_count=$(echo "$ready" | cut -d'/' -f1)
local total_count=$(echo "$ready" | cut -d'/' -f2)
if [[ "$ready_count" -eq "$total_count" ]]; then
return 0
fi
fi
done <<< "$pods"
return 1
}
# Check service health endpoint
check_service_health() {
local service=$1
local namespace=${2:-$K8S_NAMESPACE}
local health_endpoint
health_endpoint=$(get_service_health_endpoint "$service")
if [[ -z "$health_endpoint" ]]; then
log_operation "DEBUG" "No health endpoint defined for service '$service'"
return 0
fi
case "$service" in
"redis")
# Check Redis with ping
if kubectl exec -n "$namespace" deployment/redis -- redis-cli ping &>/dev/null; then
log_operation "SUCCESS" "Redis health check passed"
return 0
else
log_operation "WARNING" "Redis health check failed"
return 1
fi
;;
"minio")
# Check MinIO readiness
if kubectl exec -n "$namespace" deployment/minio -- mc ready local &>/dev/null; then
log_operation "SUCCESS" "MinIO health check passed"
return 0
else
log_operation "WARNING" "MinIO health check failed"
return 1
fi
;;
*)
# For other services, try HTTP health check
if [[ "$health_endpoint" =~ ^/.*:[0-9]+$ ]]; then
local path=$(echo "$health_endpoint" | cut -d':' -f1)
local port=$(echo "$health_endpoint" | cut -d':' -f2)
# Use port-forward to check health endpoint
local pod
pod=$(kubectl get pods -l "app=$service" -n "$namespace" --no-headers -o custom-columns=":metadata.name" | head -n1)
if [[ -n "$pod" ]]; then
if timeout 10 kubectl exec -n "$namespace" "$pod" -- curl -f -s "http://localhost:$port$path" &>/dev/null; then
log_operation "SUCCESS" "Health check passed for service '$service'"
return 0
else
log_operation "WARNING" "Health check failed for service '$service'"
return 1
fi
fi
fi
;;
esac
log_operation "DEBUG" "Could not perform health check for service '$service'"
return 0
}
# Comprehensive dependency check for a service group
check_group_dependencies() {
local group=$1
log_operation "INFO" "Checking dependencies for service group '$group'"
local services
services=$(get_services_in_group "$group")
if [[ $? -ne 0 ]]; then
return 1
fi
# Sort services by deployment order
local sorted_services
read -ra service_array <<< "$services"
sorted_services=$(sort_services_by_deploy_order "${service_array[@]}")
local all_dependencies_met=true
for service in $sorted_services; do
local dependencies
dependencies=$(get_service_dependencies "$service")
for dep in $dependencies; do
if ! check_service_ready "$dep" "$K8S_NAMESPACE" 0; then
log_operation "ERROR" "Dependency '$dep' not ready for service '$service'"
all_dependencies_met=false
fi
done
# Check app-specific dependencies
if ! check_app_dependencies "$service"; then
all_dependencies_met=false
fi
done
if [[ "$all_dependencies_met" == "true" ]]; then
log_operation "SUCCESS" "All dependencies satisfied for group '$group'"
return 0
else
log_operation "ERROR" "Some dependencies not satisfied for group '$group'"
return 1
fi
}
# Show dependency status for all services
show_dependency_status() {
echo "🔍 Dependency Status Overview:"
echo "=============================="
local all_services
all_services=$(get_services_in_group "all")
for service in $all_services; do
local status="❌ NOT READY"
local health_status=""
if check_service_ready "$service" "$K8S_NAMESPACE" 0; then
status="✅ READY"
# Check health if available
if check_service_health "$service" "$K8S_NAMESPACE"; then
health_status=" (healthy)"
else
health_status=" (unhealthy)"
fi
fi
echo " $service: $status$health_status"
done
}
# Export functions for use in other scripts
if [[ -n "$ZSH_VERSION" ]]; then
typeset -f check_service_ready wait_for_service_ready check_infrastructure_ready > /dev/null
typeset -f check_app_dependencies check_pod_ready check_service_health > /dev/null
typeset -f check_group_dependencies show_dependency_status > /dev/null
else
export -f check_service_ready wait_for_service_ready check_infrastructure_ready
export -f check_app_dependencies check_pod_ready check_service_health
export -f check_group_dependencies show_dependency_status
fi

View File

@@ -0,0 +1,589 @@
#!/bin/bash
# Kubernetes Core Functions
# File: k8s-functions.sh
# Deploy a service group
deploy_service_group() {
local group=$1
log_operation "INFO" "Deploying service group: $group"
if [[ -z "$K8S_CONFIG_DIR" ]]; then
log_operation "ERROR" "K8S_CONFIG_DIR not set"
return 1
fi
# Get YAML files for the group
local yaml_files
yaml_files=$(get_yaml_files_for_group "$group")
if [[ $? -ne 0 ]]; then
log_operation "ERROR" "Failed to get YAML files for group: $group"
return 1
fi
# Check dependencies first
if ! check_group_dependencies "$group"; then
log_operation "WARNING" "Some dependencies not satisfied, but proceeding with deployment"
fi
# Deploy each YAML file
local success=true
for yaml_file in $yaml_files; do
local full_path="$K8S_CONFIG_DIR/$yaml_file"
if [[ ! -f "$full_path" ]]; then
log_operation "ERROR" "YAML file not found: $full_path"
success=false
continue
fi
log_operation "INFO" "Applying YAML file: $yaml_file"
log_kubectl_command "kubectl apply -f $full_path"
# Apply with retry logic for namespace race condition handling
local max_attempts=3
local attempt=1
local file_success=false
while [[ $attempt -le $max_attempts ]] && [[ "$file_success" == "false" ]]; do
if kubectl apply -f "$full_path"; then
log_operation "SUCCESS" "Successfully applied: $yaml_file"
file_success=true
else
if [[ $attempt -lt $max_attempts ]]; then
log_operation "WARNING" "Attempt $attempt failed for $yaml_file, retrying after namespace sync..."
sleep 3
attempt=$((attempt + 1))
else
log_operation "ERROR" "Failed to apply $yaml_file after $max_attempts attempts"
success=false
fi
fi
done
if [[ "$file_success" == "false" ]]; then
success=false
fi
done
if [[ "$success" == "true" ]]; then
log_operation "SUCCESS" "Service group '$group' deployed successfully"
# Wait for services to be ready
wait_for_group_ready "$group"
return 0
else
log_operation "ERROR" "Failed to deploy service group '$group'"
return 1
fi
}
# Stop a service group
stop_service_group() {
local group=$1
local mode=${2:-"--keep-data"} # --keep-data, --stop-only, --delete-all
log_operation "INFO" "Stopping service group: $group (mode: $mode)"
local services
services=$(get_services_in_group "$group")
if [[ $? -ne 0 ]]; then
return 1
fi
# Sort services in reverse deployment order for graceful shutdown
local service_array
read -ra service_array <<< "$services"
local sorted_services
sorted_services=$(sort_services_by_deploy_order "${service_array[@]}")
# Reverse the order
local reversed_services=()
local service_list=($sorted_services)
for ((i=${#service_list[@]}-1; i>=0; i--)); do
reversed_services+=("${service_list[i]}")
done
local success=true
for service in "${reversed_services[@]}"; do
if ! stop_individual_service "$service" "$mode"; then
success=false
fi
done
if [[ "$success" == "true" ]]; then
log_operation "SUCCESS" "Service group '$group' stopped successfully"
return 0
else
log_operation "ERROR" "Failed to stop some services in group '$group'"
return 1
fi
}
# Start a service group (for stopped services)
start_service_group() {
local group=$1
log_operation "INFO" "Starting service group: $group"
local services
services=$(get_services_in_group "$group")
if [[ $? -ne 0 ]]; then
return 1
fi
# Sort services by deployment order
local service_array
read -ra service_array <<< "$services"
local sorted_services
sorted_services=$(sort_services_by_deploy_order "${service_array[@]}")
local success=true
for service in $sorted_services; do
if ! start_individual_service "$service"; then
success=false
fi
done
if [[ "$success" == "true" ]]; then
log_operation "SUCCESS" "Service group '$group' started successfully"
return 0
else
log_operation "ERROR" "Failed to start some services in group '$group'"
return 1
fi
}
# Deploy an individual service
deploy_individual_service() {
local service=$1
local group=${2:-""}
log_operation "INFO" "Deploying individual service: $service"
# Get YAML file for the service
local yaml_file
yaml_file=$(get_yaml_file_for_service "$service")
if [[ $? -ne 0 ]]; then
return 1
fi
local full_path="$K8S_CONFIG_DIR/$yaml_file"
if [[ ! -f "$full_path" ]]; then
log_operation "ERROR" "YAML file not found: $full_path"
return 1
fi
# Check dependencies
if ! check_app_dependencies "$service"; then
log_operation "WARNING" "Dependencies not satisfied, but proceeding with deployment"
fi
log_operation "INFO" "Applying YAML file: $yaml_file for service: $service"
log_kubectl_command "kubectl apply -f $full_path"
if kubectl apply -f "$full_path"; then
log_operation "SUCCESS" "Successfully deployed service: $service"
# Wait for service to be ready
wait_for_service_ready "$service" "$K8S_NAMESPACE" 180
return 0
else
log_operation "ERROR" "Failed to deploy service: $service"
return 1
fi
}
# Stop an individual service
stop_individual_service() {
local service=$1
local mode=${2:-"--keep-data"}
log_operation "INFO" "Stopping individual service: $service (mode: $mode)"
case "$mode" in
"--keep-data")
# Scale deployment to 0 but keep everything else
log_kubectl_command "kubectl scale deployment $service --replicas=0 -n $K8S_NAMESPACE"
if kubectl scale deployment "$service" --replicas=0 -n "$K8S_NAMESPACE" 2>/dev/null; then
log_operation "SUCCESS" "Scaled down service: $service"
else
log_operation "WARNING" "Failed to scale down service: $service (may not exist)"
fi
;;
"--stop-only")
# Same as keep-data for Kubernetes
log_kubectl_command "kubectl scale deployment $service --replicas=0 -n $K8S_NAMESPACE"
if kubectl scale deployment "$service" --replicas=0 -n "$K8S_NAMESPACE" 2>/dev/null; then
log_operation "SUCCESS" "Stopped service: $service"
else
log_operation "WARNING" "Failed to stop service: $service (may not exist)"
fi
;;
"--delete-all")
# Delete the deployment and associated resources
log_kubectl_command "kubectl delete deployment $service -n $K8S_NAMESPACE"
if kubectl delete deployment "$service" -n "$K8S_NAMESPACE" 2>/dev/null; then
log_operation "SUCCESS" "Deleted deployment: $service"
else
log_operation "WARNING" "Failed to delete deployment: $service (may not exist)"
fi
# Also delete service if it exists
log_kubectl_command "kubectl delete service ${service}-service -n $K8S_NAMESPACE"
kubectl delete service "${service}-service" -n "$K8S_NAMESPACE" 2>/dev/null || true
;;
*)
log_operation "ERROR" "Unknown stop mode: $mode"
return 1
;;
esac
return 0
}
# Start an individual service (restore replicas)
start_individual_service() {
local service=$1
log_operation "INFO" "Starting individual service: $service"
# Check if deployment exists
if ! kubectl get deployment "$service" -n "$K8S_NAMESPACE" &>/dev/null; then
log_operation "ERROR" "Deployment '$service' does not exist. Use deploy function instead."
return 1
fi
# Get the original replica count (assuming 1 if not specified)
local desired_replicas=1
# For services that typically have multiple replicas
case "$service" in
"eveai-workers"|"eveai-chat-workers")
desired_replicas=2
;;
esac
log_kubectl_command "kubectl scale deployment $service --replicas=$desired_replicas -n $K8S_NAMESPACE"
if kubectl scale deployment "$service" --replicas="$desired_replicas" -n "$K8S_NAMESPACE"; then
log_operation "SUCCESS" "Started service: $service with $desired_replicas replicas"
# Wait for service to be ready
wait_for_service_ready "$service" "$K8S_NAMESPACE" 180
return 0
else
log_operation "ERROR" "Failed to start service: $service"
return 1
fi
}
# Wait for a service group to be ready
wait_for_group_ready() {
local group=$1
local timeout=${2:-300}
log_operation "INFO" "Waiting for service group '$group' to be ready"
local services
services=$(get_services_in_group "$group")
if [[ $? -ne 0 ]]; then
return 1
fi
local all_ready=true
for service in $services; do
if ! wait_for_service_ready "$service" "$K8S_NAMESPACE" "$timeout"; then
all_ready=false
log_operation "WARNING" "Service '$service' in group '$group' failed to become ready"
fi
done
if [[ "$all_ready" == "true" ]]; then
log_operation "SUCCESS" "All services in group '$group' are ready"
return 0
else
log_operation "ERROR" "Some services in group '$group' failed to become ready"
return 1
fi
}
# Get service status
get_service_status() {
local service=$1
local namespace=${2:-$K8S_NAMESPACE}
if ! kubectl get deployment "$service" -n "$namespace" &>/dev/null; then
echo "NOT_DEPLOYED"
return 1
fi
local ready_replicas
ready_replicas=$(kubectl get deployment "$service" -n "$namespace" -o jsonpath='{.status.readyReplicas}' 2>/dev/null)
local desired_replicas
desired_replicas=$(kubectl get deployment "$service" -n "$namespace" -o jsonpath='{.spec.replicas}' 2>/dev/null)
if [[ -z "$ready_replicas" ]]; then
ready_replicas=0
fi
if [[ -z "$desired_replicas" ]]; then
desired_replicas=0
fi
if [[ "$desired_replicas" -eq 0 ]]; then
echo "STOPPED"
elif [[ "$ready_replicas" -eq "$desired_replicas" && "$ready_replicas" -gt 0 ]]; then
echo "RUNNING"
elif [[ "$ready_replicas" -gt 0 ]]; then
echo "PARTIAL"
else
echo "STARTING"
fi
}
# Show detailed service status
show_service_status() {
local service=${1:-""}
if [[ -n "$service" ]]; then
# Show status for specific service
echo "🔍 Status for service: $service"
echo "================================"
local status
status=$(get_service_status "$service")
echo "Status: $status"
if kubectl get deployment "$service" -n "$K8S_NAMESPACE" &>/dev/null; then
echo ""
echo "Deployment details:"
kubectl get deployment "$service" -n "$K8S_NAMESPACE"
echo ""
echo "Pod details:"
kubectl get pods -l "app=$service" -n "$K8S_NAMESPACE"
echo ""
echo "Recent events:"
kubectl get events --field-selector involvedObject.name="$service" -n "$K8S_NAMESPACE" --sort-by='.lastTimestamp' | tail -5
else
echo "Deployment not found"
fi
else
# Show status for all services
echo "🔍 Service Status Overview:"
echo "=========================="
local all_services
all_services=$(get_services_in_group "all")
for svc in $all_services; do
local status
status=$(get_service_status "$svc")
local status_icon
case "$status" in
"RUNNING") status_icon="✅" ;;
"PARTIAL") status_icon="⚠️" ;;
"STARTING") status_icon="🔄" ;;
"STOPPED") status_icon="⏹️" ;;
"NOT_DEPLOYED") status_icon="❌" ;;
*) status_icon="❓" ;;
esac
echo " $svc: $status_icon $status"
done
fi
}
# Restart a service (stop and start)
restart_service() {
local service=$1
log_operation "INFO" "Restarting service: $service"
if ! stop_individual_service "$service" "--stop-only"; then
log_operation "ERROR" "Failed to stop service: $service"
return 1
fi
sleep 5
if ! start_individual_service "$service"; then
log_operation "ERROR" "Failed to start service: $service"
return 1
fi
log_operation "SUCCESS" "Successfully restarted service: $service"
}
# Test service connectivity via Ingress
test_connectivity_ingress() {
log_operation "INFO" "Testing Ingress connectivity..."
# Test Ingress endpoints
local endpoints=(
"http://minty.ask-eve-ai-local.com:3080/admin/"
"http://minty.ask-eve-ai-local.com:3080/api/healthz/ready"
"http://minty.ask-eve-ai-local.com:3080/chat-client/"
"http://minty.ask-eve-ai-local.com:3080/static/"
"http://localhost:3009" # MinIO Console (direct)
"http://localhost:3010" # Prometheus (direct)
"http://localhost:3012" # Grafana (direct)
)
local success_count=0
local total_count=${#endpoints[@]}
for endpoint in "${endpoints[@]}"; do
log_operation "INFO" "Testing $endpoint..."
if curl -f -s --max-time 10 "$endpoint" > /dev/null; then
log_operation "SUCCESS" "$endpoint is responding"
((success_count++))
else
log_operation "WARNING" "$endpoint is not responding (may still be starting up)"
fi
done
echo ""
log_operation "INFO" "Connectivity test completed: $success_count/$total_count endpoints responding"
if [[ $success_count -eq $total_count ]]; then
log_operation "SUCCESS" "All endpoints are responding"
return 0
elif [[ $success_count -gt 0 ]]; then
log_operation "WARNING" "Some endpoints are not responding"
return 1
else
log_operation "ERROR" "No endpoints are responding"
return 2
fi
}
# Show connection information for Ingress setup
show_connection_info() {
echo ""
echo "=================================================="
log_operation "SUCCESS" "EveAI $K8S_ENVIRONMENT Cluster Connection Info"
echo "=================================================="
echo ""
echo "🌐 Service URLs:"
echo " Main Application (via Ingress only):"
echo " • Main App: http://minty.ask-eve-ai-local.com:3080/admin/"
echo " • API: http://minty.ask-eve-ai-local.com:3080/api/"
echo " • Chat Client: http://minty.ask-eve-ai-local.com:3080/chat-client/"
echo " • Static Files: http://minty.ask-eve-ai-local.com:3080/static/"
echo ""
echo " Infrastructure (direct NodePort access):"
echo " • Redis: redis://minty.ask-eve-ai-local.com:3006"
echo " • MinIO S3: http://minty.ask-eve-ai-local.com:3008"
echo " • MinIO Console: http://minty.ask-eve-ai-local.com:3009"
echo ""
echo " Monitoring (direct NodePort access):"
echo " • Prometheus: http://minty.ask-eve-ai-local.com:3010"
echo " • Grafana: http://minty.ask-eve-ai-local.com:3012"
echo ""
echo "🔑 Default Credentials:"
echo " • MinIO: minioadmin / minioadmin"
echo " • Grafana: admin / admin"
echo " • Flower: Felucia / Jungles"
echo ""
echo "🛠️ Management Commands:"
echo " • kubectl get all -n $K8S_NAMESPACE"
echo " • kubectl get ingress -n $K8S_NAMESPACE"
echo " • kubectl logs -f deployment/eveai-app -n $K8S_NAMESPACE"
echo " • kubectl describe ingress eveai-ingress -n $K8S_NAMESPACE"
echo ""
echo "🗂️ Data Persistence:"
echo " • Host data path: \$HOME/k8s-data/$K8S_ENVIRONMENT/"
echo " • Logs path: \$HOME/k8s-data/$K8S_ENVIRONMENT/logs/"
echo ""
echo "📊 Environment Details:"
echo " • Environment: $K8S_ENVIRONMENT"
echo " • Version: $K8S_VERSION"
echo " • Cluster: $K8S_CLUSTER"
echo " • Namespace: $K8S_NAMESPACE"
echo " • Config Dir: $K8S_CONFIG_DIR"
}
# Deploy all services in structured order (like deploy-all-services.sh)
deploy_all_structured() {
log_operation "INFO" "Starting structured deployment of all services"
echo ""
echo "=================================================="
echo "🚀 Deploying EveAI $K8S_ENVIRONMENT Services"
echo "=================================================="
# Stage 1: Infrastructure
log_operation "INFO" "Stage 1: Deploying infrastructure services..."
if ! deploy_service_group "infrastructure"; then
log_operation "ERROR" "Failed to deploy infrastructure services"
return 1
fi
log_operation "INFO" "Waiting for infrastructure to be ready..."
if ! wait_for_group_ready "infrastructure"; then
log_operation "ERROR" "Infrastructure services failed to become ready"
return 1
fi
sleep 5
# Stage 2: Application services
log_operation "INFO" "Stage 2: Deploying application services..."
if ! deploy_service_group "apps"; then
log_operation "ERROR" "Failed to deploy application services"
return 1
fi
log_operation "INFO" "Waiting for application services to be ready..."
if ! wait_for_group_ready "apps"; then
log_operation "WARNING" "Some application services may still be starting"
fi
sleep 5
# Stage 3: Static files and ingress
log_operation "INFO" "Stage 3: Deploying static files and ingress..."
if ! deploy_service_group "static"; then
log_operation "ERROR" "Failed to deploy static services"
return 1
fi
# Stage 4: Monitoring services
log_operation "INFO" "Stage 4: Deploying monitoring services..."
if ! deploy_service_group "monitoring"; then
log_operation "WARNING" "Failed to deploy monitoring services (continuing anyway)"
fi
sleep 10
# Final verification
log_operation "INFO" "Running final connectivity tests..."
test_connectivity_ingress
show_connection_info
log_operation "SUCCESS" "Structured deployment completed!"
return 0
}
# Export functions for use in other scripts
if [[ -n "$ZSH_VERSION" ]]; then
typeset -f deploy_service_group stop_service_group start_service_group > /dev/null
typeset -f deploy_individual_service stop_individual_service start_individual_service > /dev/null
typeset -f wait_for_group_ready get_service_status show_service_status restart_service > /dev/null
typeset -f test_connectivity_ingress show_connection_info deploy_all_structured > /dev/null
else
export -f deploy_service_group stop_service_group start_service_group
export -f deploy_individual_service stop_individual_service start_individual_service
export -f wait_for_group_ready get_service_status show_service_status restart_service
export -f test_connectivity_ingress show_connection_info deploy_all_structured
fi

View File

@@ -0,0 +1,222 @@
#!/bin/bash
# Kubernetes Logging Utilities
# File: logging-utils.sh
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Function for colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_debug() {
echo -e "${PURPLE}[DEBUG]${NC} $1"
}
print_operation() {
echo -e "${CYAN}[OPERATION]${NC} $1"
}
# Main logging function
log_operation() {
local level=$1
local message=$2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# Ensure log directory exists
if [[ -n "$K8S_LOG_DIR" ]]; then
mkdir -p "$K8S_LOG_DIR"
# Log to main operations file
echo "$timestamp [$level] $message" >> "$K8S_LOG_DIR/k8s-operations.log"
# Log errors to separate error file
if [[ "$level" == "ERROR" ]]; then
echo "$timestamp [ERROR] $message" >> "$K8S_LOG_DIR/service-errors.log"
print_error "$message"
elif [[ "$level" == "WARNING" ]]; then
print_warning "$message"
elif [[ "$level" == "SUCCESS" ]]; then
print_success "$message"
elif [[ "$level" == "DEBUG" ]]; then
print_debug "$message"
elif [[ "$level" == "OPERATION" ]]; then
print_operation "$message"
else
print_status "$message"
fi
else
# Fallback if no log directory is set
case $level in
"ERROR")
print_error "$message"
;;
"WARNING")
print_warning "$message"
;;
"SUCCESS")
print_success "$message"
;;
"DEBUG")
print_debug "$message"
;;
"OPERATION")
print_operation "$message"
;;
*)
print_status "$message"
;;
esac
fi
}
# Log kubectl command execution
log_kubectl_command() {
local command="$1"
local result="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [[ -n "$K8S_LOG_DIR" ]]; then
echo "$timestamp [KUBECTL] $command" >> "$K8S_LOG_DIR/kubectl-commands.log"
if [[ -n "$result" ]]; then
echo "$timestamp [KUBECTL_RESULT] $result" >> "$K8S_LOG_DIR/kubectl-commands.log"
fi
fi
}
# Log dependency check results
log_dependency_check() {
local service="$1"
local status="$2"
local details="$3"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [[ -n "$K8S_LOG_DIR" ]]; then
echo "$timestamp [DEPENDENCY] Service: $service, Status: $status, Details: $details" >> "$K8S_LOG_DIR/dependency-checks.log"
fi
if [[ "$status" == "READY" ]]; then
log_operation "SUCCESS" "Dependency check passed for $service"
elif [[ "$status" == "NOT_READY" ]]; then
log_operation "WARNING" "Dependency check failed for $service: $details"
else
log_operation "ERROR" "Dependency check error for $service: $details"
fi
}
# Show recent logs
show_recent_logs() {
local log_type=${1:-operations}
local lines=${2:-20}
if [[ -z "$K8S_LOG_DIR" ]]; then
echo "No log directory configured"
return 1
fi
case $log_type in
"operations"|"ops")
if [[ -f "$K8S_LOG_DIR/k8s-operations.log" ]]; then
echo "Recent operations (last $lines lines):"
tail -n "$lines" "$K8S_LOG_DIR/k8s-operations.log"
else
echo "No operations log found"
fi
;;
"errors"|"err")
if [[ -f "$K8S_LOG_DIR/service-errors.log" ]]; then
echo "Recent errors (last $lines lines):"
tail -n "$lines" "$K8S_LOG_DIR/service-errors.log"
else
echo "No error log found"
fi
;;
"kubectl"|"cmd")
if [[ -f "$K8S_LOG_DIR/kubectl-commands.log" ]]; then
echo "Recent kubectl commands (last $lines lines):"
tail -n "$lines" "$K8S_LOG_DIR/kubectl-commands.log"
else
echo "No kubectl command log found"
fi
;;
"dependencies"|"deps")
if [[ -f "$K8S_LOG_DIR/dependency-checks.log" ]]; then
echo "Recent dependency checks (last $lines lines):"
tail -n "$lines" "$K8S_LOG_DIR/dependency-checks.log"
else
echo "No dependency check log found"
fi
;;
*)
echo "Available log types: operations, errors, kubectl, dependencies"
return 1
;;
esac
}
# Clear logs
clear_logs() {
local log_type=${1:-all}
if [[ -z "$K8S_LOG_DIR" ]]; then
echo "No log directory configured"
return 1
fi
case $log_type in
"all")
rm -f "$K8S_LOG_DIR"/*.log
log_operation "INFO" "All logs cleared"
;;
"operations"|"ops")
rm -f "$K8S_LOG_DIR/k8s-operations.log"
echo "Operations log cleared"
;;
"errors"|"err")
rm -f "$K8S_LOG_DIR/service-errors.log"
echo "Error log cleared"
;;
"kubectl"|"cmd")
rm -f "$K8S_LOG_DIR/kubectl-commands.log"
echo "Kubectl command log cleared"
;;
"dependencies"|"deps")
rm -f "$K8S_LOG_DIR/dependency-checks.log"
echo "Dependency check log cleared"
;;
*)
echo "Available log types: all, operations, errors, kubectl, dependencies"
return 1
;;
esac
}
# Export functions for use in other scripts
if [[ -n "$ZSH_VERSION" ]]; then
typeset -f log_operation log_kubectl_command log_dependency_check > /dev/null
typeset -f show_recent_logs clear_logs > /dev/null
typeset -f print_status print_success print_warning print_error print_debug print_operation > /dev/null
else
export -f log_operation log_kubectl_command log_dependency_check
export -f show_recent_logs clear_logs
export -f print_status print_success print_warning print_error print_debug print_operation
fi

View File

@@ -0,0 +1,250 @@
#!/bin/bash
# Kubernetes Service Group Definitions
# File: service-groups.sh
# Service group definitions
declare -A SERVICE_GROUPS
# Infrastructure services (Redis, MinIO, Static Files)
SERVICE_GROUPS[infrastructure]="redis minio static-files-access static-files"
# Application services (all EveAI apps)
SERVICE_GROUPS[apps]="eveai-app eveai-api eveai-chat-client eveai-workers eveai-chat-workers eveai-beat eveai-entitlements"
# Monitoring services
SERVICE_GROUPS[monitoring]="prometheus grafana flower"
# All services combined
SERVICE_GROUPS[all]="redis minio static-files-access static-files eveai-app eveai-api eveai-chat-client eveai-workers eveai-chat-workers eveai-beat eveai-entitlements prometheus grafana flower"
# Service to YAML file mapping
declare -A SERVICE_YAML_FILES
# Infrastructure services
SERVICE_YAML_FILES[redis]="redis-minio-services.yaml"
SERVICE_YAML_FILES[minio]="redis-minio-services.yaml"
SERVICE_YAML_FILES[static-files-access]="static-files-access.yaml"
# Application services
SERVICE_YAML_FILES[eveai-app]="eveai-services.yaml"
SERVICE_YAML_FILES[eveai-api]="eveai-services.yaml"
SERVICE_YAML_FILES[eveai-chat-client]="eveai-services.yaml"
SERVICE_YAML_FILES[eveai-workers]="eveai-services.yaml"
SERVICE_YAML_FILES[eveai-chat-workers]="eveai-services.yaml"
SERVICE_YAML_FILES[eveai-beat]="eveai-services.yaml"
SERVICE_YAML_FILES[eveai-entitlements]="eveai-services.yaml"
# Static files service
SERVICE_YAML_FILES[static-files]="static-files-service.yaml"
# Monitoring services
SERVICE_YAML_FILES[prometheus]="monitoring-services.yaml"
SERVICE_YAML_FILES[grafana]="monitoring-services.yaml"
SERVICE_YAML_FILES[flower]="monitoring-services.yaml"
# Service deployment order (for dependencies)
declare -A SERVICE_DEPLOY_ORDER
# Infrastructure first (order 1)
SERVICE_DEPLOY_ORDER[redis]=1
SERVICE_DEPLOY_ORDER[minio]=1
SERVICE_DEPLOY_ORDER[static-files-access]=1
SERVICE_DEPLOY_ORDER[static-files]=1
# Core apps next (order 2)
SERVICE_DEPLOY_ORDER[eveai-app]=2
SERVICE_DEPLOY_ORDER[eveai-api]=2
SERVICE_DEPLOY_ORDER[eveai-chat-client]=2
SERVICE_DEPLOY_ORDER[eveai-entitlements]=2
# Workers after core apps (order 3)
SERVICE_DEPLOY_ORDER[eveai-workers]=3
SERVICE_DEPLOY_ORDER[eveai-chat-workers]=3
SERVICE_DEPLOY_ORDER[eveai-beat]=3
# Monitoring last (order 5)
SERVICE_DEPLOY_ORDER[prometheus]=5
SERVICE_DEPLOY_ORDER[grafana]=5
SERVICE_DEPLOY_ORDER[flower]=5
# Service health check endpoints
declare -A SERVICE_HEALTH_ENDPOINTS
SERVICE_HEALTH_ENDPOINTS[eveai-app]="/healthz/ready:5001"
SERVICE_HEALTH_ENDPOINTS[eveai-api]="/healthz/ready:5003"
SERVICE_HEALTH_ENDPOINTS[eveai-chat-client]="/healthz/ready:5004"
SERVICE_HEALTH_ENDPOINTS[redis]="ping"
SERVICE_HEALTH_ENDPOINTS[minio]="ready"
# Get services in a group
get_services_in_group() {
local group=$1
if [[ -n "${SERVICE_GROUPS[$group]}" ]]; then
echo "${SERVICE_GROUPS[$group]}"
else
log_operation "ERROR" "Unknown service group: $group"
local available_groups=("${!SERVICE_GROUPS[@]}")
echo "Available groups: ${available_groups[*]}"
return 1
fi
}
# Get YAML file for a service
get_yaml_file_for_service() {
local service=$1
if [[ -n "${SERVICE_YAML_FILES[$service]}" ]]; then
echo "${SERVICE_YAML_FILES[$service]}"
else
log_operation "ERROR" "No YAML file defined for service: $service"
return 1
fi
}
# Get deployment order for a service
get_service_deploy_order() {
local service=$1
echo "${SERVICE_DEPLOY_ORDER[$service]:-999}"
}
# Get health check endpoint for a service
get_service_health_endpoint() {
local service=$1
echo "${SERVICE_HEALTH_ENDPOINTS[$service]:-}"
}
# Sort services by deployment order
sort_services_by_deploy_order() {
local services=("$@")
local sorted_services=()
# Create array of service:order pairs
local service_orders=()
for service in "${services[@]}"; do
local order=$(get_service_deploy_order "$service")
service_orders+=("$order:$service")
done
# Sort by order and extract service names
IFS=$'\n' sorted_services=($(printf '%s\n' "${service_orders[@]}" | sort -n | cut -d: -f2))
echo "${sorted_services[@]}"
}
# Get services that should be deployed before a given service
get_service_dependencies() {
local target_service=$1
local target_order=$(get_service_deploy_order "$target_service")
local dependencies=()
# Find all services with lower deployment order
for service in "${!SERVICE_DEPLOY_ORDER[@]}"; do
local service_order="${SERVICE_DEPLOY_ORDER[$service]}"
if [[ "$service_order" -lt "$target_order" ]]; then
dependencies+=("$service")
fi
done
echo "${dependencies[@]}"
}
# Check if a service belongs to a group
is_service_in_group() {
local service=$1
local group=$2
local group_services="${SERVICE_GROUPS[$group]}"
if [[ " $group_services " =~ " $service " ]]; then
return 0
else
return 1
fi
}
# Get all unique YAML files for a group
get_yaml_files_for_group() {
local group=$1
local services
services=$(get_services_in_group "$group")
if [[ $? -ne 0 ]]; then
return 1
fi
local yaml_files=()
local unique_files=()
for service in $services; do
local yaml_file=$(get_yaml_file_for_service "$service")
if [[ -n "$yaml_file" ]]; then
yaml_files+=("$yaml_file")
fi
done
# Remove duplicates
IFS=$'\n' unique_files=($(printf '%s\n' "${yaml_files[@]}" | sort -u))
echo "${unique_files[@]}"
}
# Display service group information
show_service_groups() {
echo "📋 Available Service Groups:"
echo "============================"
for group in "${!SERVICE_GROUPS[@]}"; do
echo ""
echo "🔹 $group:"
local services="${SERVICE_GROUPS[$group]}"
for service in $services; do
local order=$(get_service_deploy_order "$service")
local yaml_file=$(get_yaml_file_for_service "$service")
echo "$service (order: $order, file: $yaml_file)"
done
done
}
# Validate service group configuration
validate_service_groups() {
local errors=0
echo "🔍 Validating service group configuration..."
# Check if all services have YAML files defined
for group in "${!SERVICE_GROUPS[@]}"; do
local services="${SERVICE_GROUPS[$group]}"
for service in $services; do
if [[ -z "${SERVICE_YAML_FILES[$service]}" ]]; then
log_operation "ERROR" "Service '$service' in group '$group' has no YAML file defined"
((errors++))
fi
done
done
# Check if YAML files exist
if [[ -n "$K8S_CONFIG_DIR" ]]; then
for yaml_file in "${SERVICE_YAML_FILES[@]}"; do
if [[ ! -f "$K8S_CONFIG_DIR/$yaml_file" ]]; then
log_operation "WARNING" "YAML file '$yaml_file' not found in $K8S_CONFIG_DIR"
fi
done
fi
if [[ $errors -eq 0 ]]; then
log_operation "SUCCESS" "Service group configuration is valid"
return 0
else
log_operation "ERROR" "Found $errors configuration errors"
return 1
fi
}
# Export functions for use in other scripts
if [[ -n "$ZSH_VERSION" ]]; then
typeset -f get_services_in_group get_yaml_file_for_service get_service_deploy_order > /dev/null
typeset -f get_service_health_endpoint sort_services_by_deploy_order get_service_dependencies > /dev/null
typeset -f is_service_in_group get_yaml_files_for_group show_service_groups validate_service_groups > /dev/null
else
export -f get_services_in_group get_yaml_file_for_service get_service_deploy_order
export -f get_service_health_endpoint sort_services_by_deploy_order get_service_dependencies
export -f is_service_in_group get_yaml_files_for_group show_service_groups validate_service_groups
fi

225
k8s/test-k8s-functions.sh Executable file
View File

@@ -0,0 +1,225 @@
#!/bin/bash
# Test script for k8s_env_switch.sh functionality
# File: test-k8s-functions.sh
echo "🧪 Testing k8s_env_switch.sh functionality..."
echo "=============================================="
# Mock kubectl and kind commands for testing
kubectl() {
echo "Mock kubectl called with: $*"
case "$1" in
"config")
if [[ "$2" == "current-context" ]]; then
echo "kind-eveai-dev-cluster"
elif [[ "$2" == "use-context" ]]; then
return 0
fi
;;
"get")
if [[ "$2" == "deployments" ]]; then
echo "eveai-app 1/1 1 1 1d"
echo "eveai-api 1/1 1 1 1d"
elif [[ "$2" == "pods,services,ingress" ]]; then
echo "NAME READY STATUS RESTARTS AGE"
echo "pod/eveai-app-xxx 1/1 Running 0 1d"
echo "pod/eveai-api-xxx 1/1 Running 0 1d"
fi
;;
*)
return 0
;;
esac
}
kind() {
echo "Mock kind called with: $*"
case "$1" in
"get")
if [[ "$2" == "clusters" ]]; then
echo "eveai-dev-cluster"
fi
;;
*)
return 0
;;
esac
}
# Export mock functions
export -f kubectl kind
# Test 1: Source the main script with mocked tools
echo ""
echo "Test 1: Sourcing k8s_env_switch.sh with dev environment"
echo "--------------------------------------------------------"
# Temporarily modify the script to skip tool checks for testing
cp k8s/k8s_env_switch.sh k8s/k8s_env_switch.sh.backup
# Create a test version that skips tool checks
sed 's/if ! command -v kubectl/if false \&\& ! command -v kubectl/' k8s/k8s_env_switch.sh.backup > k8s/k8s_env_switch_test.sh
sed -i 's/if ! command -v kind/if false \&\& ! command -v kind/' k8s/k8s_env_switch_test.sh
# Source the test version
if source k8s/k8s_env_switch_test.sh dev 2>/dev/null; then
echo "✅ Successfully sourced k8s_env_switch.sh"
else
echo "❌ Failed to source k8s_env_switch.sh"
exit 1
fi
# Test 2: Check if environment variables are set
echo ""
echo "Test 2: Checking environment variables"
echo "--------------------------------------"
expected_vars=(
"K8S_ENVIRONMENT:dev"
"K8S_VERSION:latest"
"K8S_CLUSTER:kind-eveai-dev-cluster"
"K8S_NAMESPACE:eveai-dev"
"K8S_CONFIG_DIR:$PWD/k8s/dev"
)
for var_check in "${expected_vars[@]}"; do
var_name=$(echo "$var_check" | cut -d: -f1)
expected_value=$(echo "$var_check" | cut -d: -f2-)
actual_value=$(eval echo \$$var_name)
if [[ "$actual_value" == "$expected_value" ]]; then
echo "$var_name = $actual_value"
else
echo "$var_name = $actual_value (expected: $expected_value)"
fi
done
# Test 3: Check if core functions are defined
echo ""
echo "Test 3: Checking if core functions are defined"
echo "-----------------------------------------------"
core_functions=(
"kup"
"kdown"
"kstop"
"kstart"
"kps"
"klogs"
"krefresh"
"kup-app"
"kup-api"
"cluster-status"
)
for func in "${core_functions[@]}"; do
if declare -f "$func" > /dev/null; then
echo "✅ Function $func is defined"
else
echo "❌ Function $func is NOT defined"
fi
done
# Test 4: Check if supporting functions are loaded
echo ""
echo "Test 4: Checking if supporting functions are loaded"
echo "----------------------------------------------------"
supporting_functions=(
"log_operation"
"get_services_in_group"
"check_service_ready"
"deploy_service_group"
)
for func in "${supporting_functions[@]}"; do
if declare -f "$func" > /dev/null; then
echo "✅ Supporting function $func is loaded"
else
echo "❌ Supporting function $func is NOT loaded"
fi
done
# Test 5: Test service group definitions
echo ""
echo "Test 5: Testing service group functionality"
echo "--------------------------------------------"
if declare -f get_services_in_group > /dev/null; then
echo "Testing get_services_in_group function:"
# Test infrastructure group
if infrastructure_services=$(get_services_in_group "infrastructure" 2>/dev/null); then
echo "✅ Infrastructure services: $infrastructure_services"
else
echo "❌ Failed to get infrastructure services"
fi
# Test apps group
if apps_services=$(get_services_in_group "apps" 2>/dev/null); then
echo "✅ Apps services: $apps_services"
else
echo "❌ Failed to get apps services"
fi
# Test invalid group
if get_services_in_group "invalid" 2>/dev/null; then
echo "❌ Should have failed for invalid group"
else
echo "✅ Correctly failed for invalid group"
fi
else
echo "❌ get_services_in_group function not available"
fi
# Test 6: Test basic function calls (without actual kubectl operations)
echo ""
echo "Test 6: Testing basic function calls"
echo "-------------------------------------"
# Test kps function
echo "Testing kps function:"
if kps 2>/dev/null; then
echo "✅ kps function executed successfully"
else
echo "❌ kps function failed"
fi
# Test klogs function (should show available services)
echo ""
echo "Testing klogs function (no arguments):"
if klogs 2>/dev/null; then
echo "✅ klogs function executed successfully"
else
echo "❌ klogs function failed"
fi
# Test cluster-status function
echo ""
echo "Testing cluster-status function:"
if cluster-status 2>/dev/null; then
echo "✅ cluster-status function executed successfully"
else
echo "❌ cluster-status function failed"
fi
# Cleanup
echo ""
echo "Cleanup"
echo "-------"
rm -f k8s/k8s_env_switch_test.sh
echo "✅ Cleaned up test files"
echo ""
echo "🎉 Test Summary"
echo "==============="
echo "The k8s_env_switch.sh script has been successfully implemented with:"
echo "• ✅ Environment switching functionality"
echo "• ✅ Service group definitions"
echo "• ✅ Individual service management functions"
echo "• ✅ Dependency checking system"
echo "• ✅ Comprehensive logging system"
echo "• ✅ Cluster management functions"
echo ""
echo "The script is ready for use with a running Kubernetes cluster!"
echo "Usage: source k8s/k8s_env_switch.sh dev"

11
scripts/sync_evie_to_minty.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
rsync -avzm --delete \
--include='*/' \
--include='*.sh' \
--include='*.yaml' \
--exclude='*' \
../docker/ minty:/home/pieter/bin/evie/docker/
rsync -avz --delete ../k8s/ minty:/home/pieter/bin/evie/k8s/