Skip to content
Merged
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
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ jobs:
- name: Run notification tests
run: ./eoapi-cli test notification

- name: Run autoscaling tests
run: ./eoapi-cli test autoscaling

- name: Debug failed deployment
if: failure()
run: ./eoapi-cli deployment debug
Expand Down
29 changes: 24 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,34 @@ repos:
hooks:
- id: actionlint

# Python type checking for test files
# Python linting and formatting
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
hooks:
# Linter
- id: ruff
name: ruff lint
args: ['--fix']
files: ^tests\/.*\.py$
# Formatter
- id: ruff-format
name: ruff format
files: ^tests\/.*\.py$

# Python type checking for all test files
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
name: mypy strict mode for tests
args: ['--strict', '--ignore-missing-imports']
files: ^tests\/integration\/.*\.py$
additional_dependencies: ['types-psycopg2', 'httpx', 'pytest', 'types-requests']
name: mypy type checking for tests
args: ['--ignore-missing-imports', '--no-strict-optional']
files: ^tests\/.*\.py$
additional_dependencies:
- 'types-psycopg2'
- 'types-requests'
- 'httpx'
- 'pytest'
- 'kubernetes'

# Fast Helm syntax check only
- repo: local
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Added support for annotations on the PgSTAC bootstrap job via `pgstacBootstrap.jobAnnotations` in values.yaml [#381](https://github.com/developmentseed/eoapi-k8s/pull/381)
- Added load testing scripts [#373](https://github.com/developmentseed/eoapi-k8s/pull/373)

### Fixed

Expand Down
12 changes: 10 additions & 2 deletions eoapi-cli
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ readonly COMMANDS=(
"cluster"
"deployment"
"test"
"load"
"ingest"
"docs"
)
Expand All @@ -38,6 +39,7 @@ COMMANDS:
cluster Manage local Kubernetes clusters for development
deployment Deploy and manage eoAPI instances
test Run tests (helm, integration, autoscaling)
load Run load testing scenarios
ingest Load sample data into eoAPI services
docs Generate and serve documentation

Expand All @@ -56,8 +58,11 @@ EXAMPLES:
# Run integration tests only
eoapi-cli test integration

# Run autoscaling tests only
eoapi-cli test autoscaling
# Run load tests
eoapi-cli load all

# Run autoscaling load tests only
eoapi-cli load autoscaling

# Ingest sample data
eoapi-cli ingest sample-data
Expand Down Expand Up @@ -99,6 +104,9 @@ get_command_script() {
test)
echo "${SCRIPTS_DIR}/test.sh"
;;
load)
echo "${SCRIPTS_DIR}/load.sh"
;;
ingest)
echo "${SCRIPTS_DIR}/ingest.sh"
;;
Expand Down
146 changes: 143 additions & 3 deletions scripts/lib/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,151 @@ cleanup_on_exit() {

trap cleanup_on_exit EXIT

# Export functions
get_base_url() {
local namespace="${1:-eoapi}"

# Try localhost first (most common in local dev)
if curl -s -f -m 3 "http://localhost/stac" >/dev/null 2>&1; then
echo "http://localhost"
return 0
fi

# Try ingress if configured
local host
host=$(kubectl get ingress -n "$namespace" -o jsonpath='{.items[0].spec.rules[0].host}' 2>/dev/null || echo "")
if [[ -n "$host" ]] && curl -s -f -m 3 "http://$host/stac" >/dev/null 2>&1; then
echo "http://$host"
return 0
fi

return 1
}

validate_autoscaling_environment() {
local namespace="$1"

validate_cluster || return 1
validate_namespace "$namespace" || return 1

# Check HPA exists
if ! kubectl get hpa -n "$namespace" >/dev/null 2>&1 || [[ $(kubectl get hpa -n "$namespace" --no-headers 2>/dev/null | wc -l) -eq 0 ]]; then
log_error "No HPA resources found. Deploy with autoscaling enabled."
return 1
fi

# Check metrics server
if ! kubectl get deployment -A | grep -q metrics-server; then
log_error "metrics-server required for autoscaling tests"
return 1
fi

return 0
}

validate_python_environment() {
if ! command_exists python3; then
log_error "python3 is required but not found"
log_info "Install python3 to continue"
return 1
fi

log_debug "Python3 environment validated"
return 0
}

install_python_requirements() {
local requirements_file="$1"
local project_root="${2:-}"

local full_path="$requirements_file"
[[ -n "$project_root" ]] && full_path="$project_root/$requirements_file"

[[ ! -f "$full_path" ]] && { log_error "Requirements file not found: $full_path"; return 1; }

# Already in a venv? Just install
if [[ -n "${VIRTUAL_ENV:-}" ]]; then
log_debug "Using existing virtual environment: $VIRTUAL_ENV"
if python3 -m pip install -q -r "$full_path"; then
return 0
else
log_error "Failed to install requirements in existing venv"
return 1
fi
fi

# Check if .venv exists and activate it
local venv_dir="${project_root:-.}/.venv"
if [[ -d "$venv_dir" ]]; then
log_debug "Activating existing venv: $venv_dir"
# shellcheck source=/dev/null
if source "$venv_dir/bin/activate"; then
if python3 -m pip install -q -r "$full_path"; then
return 0
else
log_warn "Failed to install in existing venv, will recreate"
deactivate 2>/dev/null || true
rm -rf "$venv_dir"
fi
else
log_warn "Failed to activate venv, will recreate"
rm -rf "$venv_dir"
fi
fi

# Create new venv (prefer uv for speed)
log_info "Creating virtual environment at $venv_dir..."
if command_exists uv; then
if uv venv "$venv_dir" >/dev/null 2>&1; then
# shellcheck source=/dev/null
source "$venv_dir/bin/activate" || { log_error "Failed to activate uv venv"; return 1; }
if uv pip install -q -r "$full_path"; then
return 0
else
log_error "uv pip install failed"
return 1
fi
fi
log_warn "uv venv creation failed, falling back to python3 -m venv"
fi

if python3 -m venv "$venv_dir" 2>&1; then
# shellcheck source=/dev/null
source "$venv_dir/bin/activate" || { log_error "Failed to activate venv"; return 1; }
if python3 -m pip install -q -r "$full_path"; then
return 0
else
log_error "pip install failed"
return 1
fi
fi

log_error "Failed to create virtual environment"
log_info "Try manually: python3 -m venv .venv && source .venv/bin/activate && pip install -r $requirements_file"
return 1
}

validate_python_with_requirements() {
local requirements_file="${1:-}"
local project_root="${2:-}"

validate_python_environment || return 1

if [[ -n "$requirements_file" ]]; then
install_python_requirements "$requirements_file" "$project_root" || {
log_warn "Python requirements installation failed, but continuing..."
return 0 # Don't fail the entire operation
}
fi

return 0
}

# Export all functions for use in other scripts
export -f log_info log_success log_warn log_error log_debug
export -f command_exists validate_tools check_requirements validate_cluster
export -f is_ci validate_namespace
export -f is_ci validate_namespace get_base_url
export -f detect_release_name detect_namespace
export -f wait_for_pods validate_eoapi_deployment
export -f wait_for_pods validate_eoapi_deployment validate_autoscaling_environment
export -f preflight_deploy preflight_ingest preflight_test
export -f validate_python_environment install_python_requirements validate_python_with_requirements
export -f show_standard_options
Loading
Loading