Test Database Stack (#737)
Lint Checks / Run linter (push) Successful in 33s
Publish / Build and publish (push) Successful in 1m2s
Webapp Test / Run webapp test suite (push) Successful in 3m5s
Deploy Test / Run deploy test suite (push) Successful in 4m20s
Smoke Test / Run basic test suite (push) Successful in 4m50s

Reviewed-on: #737
This commit was merged in pull request #737.
This commit is contained in:
2024-02-15 05:26:29 +00:00
parent b59beb66eb
commit d9bb6b3588
15 changed files with 337 additions and 5 deletions
@@ -0,0 +1,20 @@
services:
database:
image: cerc/test-database-container:local
restart: always
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: "test-user"
POSTGRES_DB: "test-db"
POSTGRES_PASSWORD: "password"
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
ports:
- "5432"
test-client:
image: cerc/test-database-client:local
volumes:
db-data:
@@ -0,0 +1,12 @@
FROM ubuntu:latest
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && export DEBCONF_NOWARNINGS="yes" && \
apt-get install -y software-properties-common && \
apt-get install -y postgresql-client && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
EXPOSE 80
COPY run.sh /app/run.sh
ENTRYPOINT ["/app/run.sh"]
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Build cerc/test-container
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
docker build -t cerc/test-database-client:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} $SCRIPT_DIR
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
set -e
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
set -x
fi
# TODO derive this from config
database_url="postgresql://test-user:password@localhost:5432/test-db"
psql_command="psql ${database_url}"
program_name="Database test client:"
wait_for_database_up () {
for i in {1..50}
do
${psql_command} -c "select 1;"
psql_succeeded=$?
if [[ ${psql_succeeded} == 0 ]]; then
# if ready, return
echo "${program_name} database up"
return
else
# if not ready, wait
echo "${program_name} waiting for database: ${i}"
sleep 5
fi
done
# Timed out, error exit
echo "${program_name} waiting for database: FAILED"
exit 1
}
# Used to synchronize with the test runner
notify_test_complete () {
echo "${program_name} test complete"
}
does_test_data_exist () {
query_result=$(${psql_command} -t -c "select count(*) from test_table_1 where key_column = 'test_key_1';" | head -1 | tr -d ' ')
if [[ "${query_result}" == "1" ]]; then
return 0
else
return 1
fi
}
create_test_data () {
${psql_command} -c "create table test_table_1 (key_column text, value_column text, primary key(key_column));"
${psql_command} -c "insert into test_table_1 values ('test_key_1', 'test_value_1');"
}
wait_forever() {
# Loop to keep docker/k8s happy since this is the container entrypoint
while :; do sleep 600; done
}
wait_for_database_up
# Check if the test database content exists already
if does_test_data_exist; then
# If so, log saying so. Test harness will look for this log output
echo "${program_name} test data already exists"
else
# Otherwise log saying the content was not present
echo "${program_name} test data does not exist"
echo "${program_name} creating test data"
# then create it
create_test_data
fi
notify_test_complete
wait_forever
@@ -0,0 +1,3 @@
FROM postgres:16-bullseye
EXPOSE 5432
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Build cerc/test-container
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
docker build -t cerc/test-database-container:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} $SCRIPT_DIR
@@ -0,0 +1,3 @@
# Test Database Stack
A stack with a database for test/demo purposes.
@@ -0,0 +1,9 @@
version: "1.0"
name: test
description: "A test database stack"
repos:
containers:
- cerc/test-database-container
- cerc/test-database-client
pods:
- test-database
+9 -2
View File
@@ -20,7 +20,8 @@ from kubernetes import client, config
from stack_orchestrator import constants
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
from stack_orchestrator.deploy.k8s.helpers import create_cluster, destroy_cluster, load_images_into_kind
from stack_orchestrator.deploy.k8s.helpers import pods_in_deployment, log_stream_from_string, generate_kind_config
from stack_orchestrator.deploy.k8s.helpers import pods_in_deployment, containers_in_pod, log_stream_from_string
from stack_orchestrator.deploy.k8s.helpers import generate_kind_config
from stack_orchestrator.deploy.k8s.cluster_info import ClusterInfo
from stack_orchestrator.opts import opts
from stack_orchestrator.deploy.deployment_context import DeploymentContext
@@ -382,9 +383,15 @@ class K8sDeployer(Deployer):
log_data = "******* Pods not running ********\n"
else:
k8s_pod_name = pods[0]
containers = containers_in_pod(self.core_api, k8s_pod_name)
# If the pod is not yet started, the logs request below will throw an exception
try:
log_data = self.core_api.read_namespaced_pod_log(k8s_pod_name, namespace="default", container="test")
log_data = ""
for container in containers:
container_log = self.core_api.read_namespaced_pod_log(k8s_pod_name, namespace="default", container=container)
container_log_lines = container_log.splitlines()
for line in container_log_lines:
log_data += f"{container}: {line}\n"
except client.exceptions.ApiException as e:
if opts.o.debug:
print(f"Error from read_namespaced_pod_log: {e}")
+11
View File
@@ -62,6 +62,17 @@ def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str):
return pods
def containers_in_pod(core_api: client.CoreV1Api, pod_name: str):
containers = []
pod_response = core_api.read_namespaced_pod(pod_name, namespace="default")
if opts.o.debug:
print(f"pod_response: {pod_response}")
pod_containers = pod_response.spec.containers
for pod_container in pod_containers:
containers.append(pod_container.name)
return containers
def log_stream_from_string(s: str):
# Note response has to be UTF-8 encoded because the caller expects to decode it
yield ("ignore", s.encode())
@@ -26,7 +26,7 @@ import importlib.resources
from pathlib import Path
import yaml
from stack_orchestrator.constants import stack_file_name
from stack_orchestrator.util import include_exclude_check, stack_is_external, error_exit
from stack_orchestrator.util import include_exclude_check, stack_is_external, error_exit, warn_exit
class GitProgress(git.RemoteProgress):
@@ -249,8 +249,8 @@ def command(ctx, include, exclude, git_ssh, check_only, pull, branches, branches
error_exit(f"stack {stack} does not exist")
with stack_file_path:
stack_config = yaml.safe_load(open(stack_file_path, "r"))
if "repos" not in stack_config:
error_exit(f"stack {stack} does not define any repositories")
if "repos" not in stack_config or stack_config["repos"] is None:
warn_exit(f"stack {stack} does not define any repositories")
else:
repos_in_scope = stack_config["repos"]
else:
+5
View File
@@ -189,5 +189,10 @@ def error_exit(s):
sys.exit(1)
def warn_exit(s):
print(f"WARN: {s}")
sys.exit(0)
def env_var_map_from_file(file: Path) -> Mapping[str, str]:
return dotenv_values(file)