Skip to content

WIP: Implement CI workflow for containerized deployment testing (#48) #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Container Environment CI

on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Install Docker Compose
run: |
mkdir -p ~/.docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose

- name: Build and start services
run: docker compose -f docker-compose-test.yml build

- name: Clean up
if: always()
run: docker compose -f docker-compose-test.yml down
25 changes: 25 additions & 0 deletions .github/workflows/container-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Container CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and test container
run: |
docker compose -f docker-compose.yml -f docker-compose-test.yml build
docker compose -f docker-compose.yml -f docker-compose-test.yml up --abort-on-container-exit

- name: Clean up
if: always()
run: docker compose -f docker-compose.yml -f docker-compose-test.yml down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ k8sgpt_*.deb

# Docs
microk8s_setup_and_ai_interaction_command_line_instructions.md
/helm-chart/README.md
/helm-chart/README.md

# Models
models/*.gguf
48 changes: 48 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Stage 1: Builder
FROM rust:1.84-slim-bookworm as builder

# Install build dependencies
RUN apt-get update && apt-get install -y \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*

# Create a new empty shell project
WORKDIR /usr/src/app
COPY Cargo.toml Cargo.lock ./

# Build dependencies - this is the caching Docker layer
RUN mkdir src && \
echo "fn main() {}" > src/main.rs && \
cargo build --release && \
rm -rf src

# Build actual source code
COPY src ./src
RUN touch src/main.rs && cargo build --release

# Stage 2: Runtime
FROM debian:bookworm-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd -r appuser && useradd -r -g appuser appuser \
&& mkdir -p /home/appuser/.kube \
&& chown -R appuser:appuser /home/appuser \
&& chmod 700 /home/appuser/.kube

# Copy the binary from builder
COPY --from=builder /usr/src/app/target/release/kube_app /usr/local/bin/

# Set proper permissions
RUN chown appuser:appuser /usr/local/bin/kube_app

# Use non-root user
USER appuser

# Run the binary
CMD ["kube_app"]

56 changes: 56 additions & 0 deletions docker-compose-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
version: "3.8"

services:
# localai:
# extends:
# file: docker-compose.yml
# service: localai
# environment:
# - LOCALAI_DEBUG=true
# - TEST_MODE=true
# healthcheck:
# test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
# interval: 10s
# timeout: 5s
# retries: 3
# start_period: 20s

# k8sgpt:
# extends:
# file: docker-compose.yml
# service: k8sgpt
# environment:
# - K8SGPT_TEST_MODE=true
# - K8SGPT_LOG_LEVEL=debug
# healthcheck:
# test: ["CMD", "k8sgpt version"]
# interval: 10s
# timeout: 5s
# retries: 3

app:
extends:
file: docker-compose.yml
service: app
environment:
- RUST_LOG=debug
- TEST_ENV=true
- MOCK_K8S_API=true
- KUBECONFIG=/home/appuser/.kube/config
volumes:
- ./test/mock-kubeconfig:/home/appuser/.kube/config:ro
healthcheck:
test: ["CMD", "/app/health_check"]
interval: 10s
timeout: 5s
retries: 3

volumes:
test-models:
driver: local
test-output:
driver: local

networks:
test-network:
driver: bridge
9 changes: 9 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: '3.8'

services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ${KUBECONFIG:-~/.kube/config}:/home/appuser/.kube/config:ro
112 changes: 112 additions & 0 deletions scripts/health_check.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env zsh

# Enable error handling
setopt ERR_EXIT PIPE_FAIL NO_UNSET WARN_CREATE_GLOBAL

# Define colors for output
typeset -A colors
colors=(
'reset' $'\033[0m'
'red' $'\033[31m'
'green' $'\033[32m'
'yellow' $'\033[33m'
'blue' $'\033[34m'
)

# Constants
typeset -r MAX_RETRIES=3
typeset -r TIMEOUT=10
typeset -r RETRY_DELAY=5

# Helper functions
function print_status() {
local message=$1
local status=$2
if [[ $status -eq 0 ]]; then
print "${colors[green]}✓ ${message}${colors[reset]}"
else
print "${colors[red]}✗ ${message}${colors[reset]}"
return 1
fi
}

function check_command() {
local cmd=$1
which $cmd &>/dev/null || {
print "${colors[red]}Error: $cmd not found${colors[reset]}"
return 1
}
}

function check_localai() {
local retries=0
while (( retries < MAX_RETRIES )); do
if curl -s --max-time $TIMEOUT "http://localhost:8080/health" | grep -q "ok"; then
print_status "LocalAI service is healthy" 0
return 0
fi
((retries++))
print "${colors[yellow]}Retrying LocalAI health check ($retries/$MAX_RETRIES)${colors[reset]}"
sleep $RETRY_DELAY
done
print_status "LocalAI service is not responding" 1
return 1
}

function check_k8sgpt() {
local retries=0
while (( retries < MAX_RETRIES )); do
if k8sgpt version &>/dev/null; then
print_status "K8sGPT service is healthy" 0
return 0
fi
((retries++))
print "${colors[yellow]}Retrying K8sGPT health check ($retries/$MAX_RETRIES)${colors[reset]}"
sleep $RETRY_DELAY
done
print_status "K8sGPT service is not responding" 1
return 1
}

function check_kube_app() {
local retries=0
while (( retries < MAX_RETRIES )); do
if curl -s --max-time $TIMEOUT "http://localhost:3000/health" | grep -q "ok"; then
print_status "Kube application is healthy" 0
return 0
fi
((retries++))
print "${colors[yellow]}Retrying Kube application health check ($retries/$MAX_RETRIES)${colors[reset]}"
sleep $RETRY_DELAY
done
print_status "Kube application is not responding" 1
return 1
}

# Main
function main() {
print "${colors[blue]}Starting health checks...${colors[reset]}"

# Check required commands
check_command "curl" || return 1
check_command "k8sgpt" || return 1

# Initialize status
local -i status=0

# Run health checks
check_localai || status=1
check_k8sgpt || status=1
check_kube_app || status=1

if [[ $status -eq 0 ]]; then
print "\n${colors[green]}All services are healthy!${colors[reset]}"
return 0
else
print "\n${colors[red]}One or more services are unhealthy${colors[reset]}"
return 1
fi
}

main "$@"

Loading