forked from cerc-io/stack-orchestrator
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01f9fe67ed | ||
|
|
049ffcff71 | ||
|
|
f5314a979b | ||
|
|
39f4fa4487 | ||
|
|
0b0394a940 | ||
|
|
37b9500483 | ||
|
|
3c3e582939 | ||
|
|
26d265360d | ||
|
|
f81b78cfbc | ||
|
|
d9bb6b3588 | ||
|
|
b59beb66eb | ||
|
|
65d67dba10 | ||
|
|
b22c72e715 | ||
|
|
c9444591f5 | ||
|
|
903f3b10e2 | ||
|
|
72ed2eb91a |
@@ -0,0 +1,54 @@
|
|||||||
|
name: Container Registry Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: '*'
|
||||||
|
paths:
|
||||||
|
- '!**'
|
||||||
|
- '.gitea/workflows/triggers/test-container-registry'
|
||||||
|
- '.gitea/workflows/test-container-registry.yml'
|
||||||
|
- 'tests/container-registry/run-test.sh'
|
||||||
|
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||||
|
- cron: '6 19 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: "Run contaier registry hosting test on kind/k8s"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: "Clone project repository"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
# At present the stock setup-python action fails on Linux/aarch64
|
||||||
|
# Conditional steps below workaroud this by using deadsnakes for that case only
|
||||||
|
- name: "Install Python for ARM on Linux"
|
||||||
|
if: ${{ runner.arch == 'arm64' && runner.os == 'Linux' }}
|
||||||
|
uses: deadsnakes/action@v3.0.1
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Install Python cases other than ARM on Linux"
|
||||||
|
if: ${{ ! (runner.arch == 'arm64' && runner.os == 'Linux') }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Print Python version"
|
||||||
|
run: python3 --version
|
||||||
|
- name: "Install shiv"
|
||||||
|
run: pip install shiv
|
||||||
|
- name: "Generate build version file"
|
||||||
|
run: ./scripts/create_build_tag_file.sh
|
||||||
|
- name: "Build local shiv package"
|
||||||
|
run: ./scripts/build_shiv_package.sh
|
||||||
|
- name: "Check cgroups version"
|
||||||
|
run: mount | grep cgroup
|
||||||
|
- name: "Install kind"
|
||||||
|
run: ./tests/scripts/install-kind.sh
|
||||||
|
- name: "Install Kubectl"
|
||||||
|
run: ./tests/scripts/install-kubectl.sh
|
||||||
|
- name: "Install ed" # Only needed until we remove the need to edit the spec file
|
||||||
|
run: apt update && apt install -y ed
|
||||||
|
- name: "Run container registry deployment test"
|
||||||
|
run: |
|
||||||
|
source /opt/bash-utils/cgroup-helper.sh
|
||||||
|
join_cgroup
|
||||||
|
./tests/container-registry/run-test.sh
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
name: Database Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: '*'
|
||||||
|
paths:
|
||||||
|
- '!**'
|
||||||
|
- '.gitea/workflows/triggers/test-database'
|
||||||
|
- '.gitea/workflows/test-database.yml'
|
||||||
|
- 'tests/database/run-test.sh'
|
||||||
|
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||||
|
- cron: '5 18 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: "Run database hosting test on kind/k8s"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: "Clone project repository"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
# At present the stock setup-python action fails on Linux/aarch64
|
||||||
|
# Conditional steps below workaroud this by using deadsnakes for that case only
|
||||||
|
- name: "Install Python for ARM on Linux"
|
||||||
|
if: ${{ runner.arch == 'arm64' && runner.os == 'Linux' }}
|
||||||
|
uses: deadsnakes/action@v3.0.1
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Install Python cases other than ARM on Linux"
|
||||||
|
if: ${{ ! (runner.arch == 'arm64' && runner.os == 'Linux') }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Print Python version"
|
||||||
|
run: python3 --version
|
||||||
|
- name: "Install shiv"
|
||||||
|
run: pip install shiv
|
||||||
|
- name: "Generate build version file"
|
||||||
|
run: ./scripts/create_build_tag_file.sh
|
||||||
|
- name: "Build local shiv package"
|
||||||
|
run: ./scripts/build_shiv_package.sh
|
||||||
|
- name: "Check cgroups version"
|
||||||
|
run: mount | grep cgroup
|
||||||
|
- name: "Install kind"
|
||||||
|
run: ./tests/scripts/install-kind.sh
|
||||||
|
- name: "Install Kubectl"
|
||||||
|
run: ./tests/scripts/install-kubectl.sh
|
||||||
|
- name: "Run database deployment test"
|
||||||
|
run: |
|
||||||
|
source /opt/bash-utils/cgroup-helper.sh
|
||||||
|
join_cgroup
|
||||||
|
./tests/database/run-test.sh
|
||||||
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
name: K8s Deploy Test
|
name: K8s Deploy Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: '*'
|
||||||
push:
|
push:
|
||||||
branches: '*'
|
branches: '*'
|
||||||
paths:
|
paths:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Change this file to trigger running the test-container-registry CI job
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Change this file to trigger running the test-database CI job
|
||||||
|
Trigger test run
|
||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Beginnings of a script to quickly spin up and test a deployment
|
||||||
|
if [[ -n "$CERC_SCRIPT_DEBUG" ]]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
stack_name=$1
|
||||||
|
else
|
||||||
|
stack_name="test"
|
||||||
|
fi
|
||||||
|
spec_file_name="${stack_name}-spec.yml"
|
||||||
|
deployment_dir_name="${stack_name}-deployment"
|
||||||
|
rm -f ${spec_file_name}
|
||||||
|
rm -rf ${deployment_dir_name}
|
||||||
|
laconic-so --stack ${stack_name} deploy --deploy-to compose init --output ${spec_file_name}
|
||||||
|
laconic-so --stack ${stack_name} deploy --deploy-to compose create --deployment-dir ${deployment_dir_name} --spec-file ${spec_file_name}
|
||||||
|
#laconic-so deployment --dir ${deployment_dir_name} start
|
||||||
|
#laconic-so deployment --dir ${deployment_dir_name} ps
|
||||||
|
#laconic-so deployment --dir ${deployment_dir_name} stop
|
||||||
@@ -27,7 +27,7 @@ import subprocess
|
|||||||
import click
|
import click
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from stack_orchestrator.util import include_exclude_check, get_parsed_stack_config, stack_is_external
|
from stack_orchestrator.util import include_exclude_check, get_parsed_stack_config, stack_is_external, warn_exit
|
||||||
from stack_orchestrator.base import get_npm_registry_url
|
from stack_orchestrator.base import get_npm_registry_url
|
||||||
|
|
||||||
# TODO: find a place for this
|
# TODO: find a place for this
|
||||||
@@ -164,6 +164,8 @@ def command(ctx, include, exclude, force_rebuild, extra_build_args):
|
|||||||
containers_in_scope = []
|
containers_in_scope = []
|
||||||
if stack:
|
if stack:
|
||||||
stack_config = get_parsed_stack_config(stack)
|
stack_config = get_parsed_stack_config(stack)
|
||||||
|
if "containers" not in stack_config or stack_config["containers"] is None:
|
||||||
|
warn_exit(f"stack {stack} does not define any containers")
|
||||||
containers_in_scope = stack_config['containers']
|
containers_in_scope = stack_config['containers']
|
||||||
else:
|
else:
|
||||||
containers_in_scope = all_containers
|
containers_in_scope = all_containers
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ kube_config_key = "kube-config"
|
|||||||
deploy_to_key = "deploy-to"
|
deploy_to_key = "deploy-to"
|
||||||
network_key = "network"
|
network_key = "network"
|
||||||
http_proxy_key = "http-proxy"
|
http_proxy_key = "http-proxy"
|
||||||
image_resigtry_key = "image-registry"
|
image_registry_key = "image-registry"
|
||||||
|
configmaps_key = "configmaps"
|
||||||
|
resources_key = "resources"
|
||||||
|
volumes_key = "volumes"
|
||||||
|
security_key = "security"
|
||||||
|
annotations_key = "annotations"
|
||||||
|
labels_key = "labels"
|
||||||
kind_config_filename = "kind-config.yml"
|
kind_config_filename = "kind-config.yml"
|
||||||
kube_config_filename = "kubeconfig.yml"
|
kube_config_filename = "kubeconfig.yml"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
registry:
|
||||||
|
image: registry:2.8
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
REGISTRY_LOG_LEVEL: ${REGISTRY_LOG_LEVEL}
|
||||||
|
volumes:
|
||||||
|
- registry-data:/var/lib/registry
|
||||||
|
ports:
|
||||||
|
- "5000"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
registry-data:
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
version: "3.2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mars:
|
||||||
|
image: cerc/mars-v2:local
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- URL_OSMOSIS_REST=https://lcd-osmosis.blockapsis.com
|
||||||
|
- URL_OSMOSIS_RPC=https://rpc-osmosis.blockapsis.com
|
||||||
|
- WALLET_CONNECT_ID=0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x
|
||||||
@@ -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:
|
||||||
@@ -7,11 +7,13 @@ services:
|
|||||||
CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED}
|
CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED}
|
||||||
CERC_TEST_PARAM_2: "CERC_TEST_PARAM_2_VALUE"
|
CERC_TEST_PARAM_2: "CERC_TEST_PARAM_2_VALUE"
|
||||||
volumes:
|
volumes:
|
||||||
- test-data:/data
|
- test-data-bind:/data
|
||||||
|
- test-data-auto:/data2
|
||||||
- test-config:/config:ro
|
- test-config:/config:ro
|
||||||
ports:
|
ports:
|
||||||
- "80"
|
- "80"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
test-data:
|
test-data-bind:
|
||||||
|
test-data-auto:
|
||||||
test-config:
|
test-config:
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
ports:
|
ports:
|
||||||
- "3000"
|
- "80"
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the mars-v2 image
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
docker build -t cerc/mars-v2:local -f ${CERC_REPO_BASE_DIR}/mars-v2-frontend/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/mars-v2-frontend
|
||||||
+2
-2
@@ -33,8 +33,8 @@ if [ -f ".env" ]; then
|
|||||||
rm -f $TMP_ENV
|
rm -f $TMP_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for f in $(find "$TRG_DIR" -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do
|
for f in $(find . -type f \( -regex '.*.html?' -or -regex ".*.[tj]s\(x\|on\)?$" \) | grep -v 'node_modules' | grep -v '.git'); do
|
||||||
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},();]' '\n' | egrep -o '^"CERC_RUNTIME_ENV_[^\"]+"'); do
|
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '["/\\{},();]' '\n' | tr -s "[']" '\n' | egrep -o -e '^CERC_RUNTIME_ENV_.+$' -e '^LACONIC_HOSTED_CONFIG_.+$'); do
|
||||||
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
||||||
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
||||||
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
||||||
|
|||||||
@@ -1,19 +1,38 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
set -x
|
set -x
|
||||||
fi
|
fi
|
||||||
# Test if the container's filesystem is old (run previously) or new
|
|
||||||
EXISTSFILENAME=/data/exists
|
|
||||||
echo "Test container starting"
|
echo "Test container starting"
|
||||||
if [[ -f "$EXISTSFILENAME" ]];
|
|
||||||
then
|
DATA_DEVICE=$(df | grep "/data$" | awk '{ print $1 }')
|
||||||
TIMESTAMP=`cat $EXISTSFILENAME`
|
if [[ -n "$DATA_DEVICE" ]]; then
|
||||||
echo "Filesystem is old, created: $TIMESTAMP"
|
echo "/data: MOUNTED dev=${DATA_DEVICE}"
|
||||||
else
|
else
|
||||||
echo "Filesystem is fresh"
|
echo "/data: not mounted"
|
||||||
echo `date` > $EXISTSFILENAME
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
DATA2_DEVICE=$(df | grep "/data2$" | awk '{ print $1 }')
|
||||||
|
if [[ -n "$DATA_DEVICE" ]]; then
|
||||||
|
echo "/data2: MOUNTED dev=${DATA2_DEVICE}"
|
||||||
|
else
|
||||||
|
echo "/data2: not mounted"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test if the container's filesystem is old (run previously) or new
|
||||||
|
for d in /data /data2; do
|
||||||
|
if [[ -f "$d/exists" ]];
|
||||||
|
then
|
||||||
|
TIMESTAMP=`cat $d/exists`
|
||||||
|
echo "$d filesystem is old, created: $TIMESTAMP"
|
||||||
|
else
|
||||||
|
echo "$d filesystem is fresh"
|
||||||
|
echo `date` > $d/exists
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [ -n "$CERC_TEST_PARAM_1" ]; then
|
if [ -n "$CERC_TEST_PARAM_1" ]; then
|
||||||
echo "Test-param-1: ${CERC_TEST_PARAM_1}"
|
echo "Test-param-1: ${CERC_TEST_PARAM_1}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -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
|
||||||
+5
@@ -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
|
||||||
+2
-2
@@ -18,8 +18,8 @@ if [ -f ".env" ]; then
|
|||||||
rm -f $TMP_ENV
|
rm -f $TMP_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for f in $(find . -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do
|
for f in $(find . -type f \( -regex '.*.html?' -or -regex ".*.[tj]s\(x\|on\)?$" \) | grep -v 'node_modules' | grep -v '.git'); do
|
||||||
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},();]' '\n' | egrep -o -e '^"CERC_RUNTIME_ENV_[^\"]+"' -e '^"LACONIC_HOSTED_CONFIG_[^\"]+"'); do
|
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '["/\\{},();]' '\n' | tr -s "[']" '\n' | egrep -o -e '^CERC_RUNTIME_ENV_.+$' -e '^LACONIC_HOSTED_CONFIG_.+$'); do
|
||||||
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
||||||
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
||||||
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
||||||
|
|||||||
@@ -33,4 +33,10 @@ else
|
|||||||
mv "${WORK_DIR}" "${DEST_DIR}"
|
mv "${WORK_DIR}" "${DEST_DIR}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# One special fix ...
|
||||||
|
cd "${DEST_DIR}"
|
||||||
|
for f in $(find . -type f -name '*.htm*'); do
|
||||||
|
sed -i -e 's#/LACONIC_HOSTED_CONFIG_homepage/#LACONIC_HOSTED_CONFIG_homepage/#g' "$f"
|
||||||
|
done
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -0,0 +1,673 @@
|
|||||||
|
# from: https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
|
||||||
|
# via: https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
name: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- namespaces
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- pods
|
||||||
|
- secrets
|
||||||
|
- endpoints
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingressclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resourceNames:
|
||||||
|
- ingress-nginx-leader
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- discovery.k8s.io
|
||||||
|
resources:
|
||||||
|
- endpointslices
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- endpoints
|
||||||
|
- nodes
|
||||||
|
- pods
|
||||||
|
- secrets
|
||||||
|
- namespaces
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingressclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- discovery.k8s.io
|
||||||
|
resources:
|
||||||
|
- endpointslices
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- admissionregistration.k8s.io
|
||||||
|
resources:
|
||||||
|
- validatingwebhookconfigurations
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: ingress-nginx
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: ingress-nginx
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
allow-snippet-annotations: "false"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
ipFamilies:
|
||||||
|
- IPv4
|
||||||
|
ipFamilyPolicy: SingleStack
|
||||||
|
ports:
|
||||||
|
- appProtocol: http
|
||||||
|
name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: http
|
||||||
|
- appProtocol: https
|
||||||
|
name: https
|
||||||
|
port: 443
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: https
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
type: NodePort
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- appProtocol: https
|
||||||
|
name: https-webhook
|
||||||
|
port: 443
|
||||||
|
targetPort: webhook
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
type: ClusterIP
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 0
|
||||||
|
revisionHistoryLimit: 10
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
strategy:
|
||||||
|
rollingUpdate:
|
||||||
|
maxUnavailable: 1
|
||||||
|
type: RollingUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- /nginx-ingress-controller
|
||||||
|
- --election-id=ingress-nginx-leader
|
||||||
|
- --controller-class=k8s.io/ingress-nginx
|
||||||
|
- --ingress-class=nginx
|
||||||
|
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
|
||||||
|
- --validating-webhook=:8443
|
||||||
|
- --validating-webhook-certificate=/usr/local/certificates/cert
|
||||||
|
- --validating-webhook-key=/usr/local/certificates/key
|
||||||
|
- --watch-ingress-without-class=true
|
||||||
|
- --publish-status-address=localhost
|
||||||
|
env:
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: LD_PRELOAD
|
||||||
|
value: /usr/local/lib/libmimalloc.so
|
||||||
|
image: registry.k8s.io/ingress-nginx/controller:v1.9.6@sha256:1405cc613bd95b2c6edd8b2a152510ae91c7e62aea4698500d23b2145960ab9c
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /wait-shutdown
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 1
|
||||||
|
name: controller
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
hostPort: 80
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 443
|
||||||
|
hostPort: 443
|
||||||
|
name: https
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 8443
|
||||||
|
name: webhook
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
failureThreshold: 3
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 1
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 90Mi
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
add:
|
||||||
|
- NET_BIND_SERVICE
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: false
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 101
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /usr/local/certificates/
|
||||||
|
name: webhook-cert
|
||||||
|
readOnly: true
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
nodeSelector:
|
||||||
|
ingress-ready: "true"
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
serviceAccountName: ingress-nginx
|
||||||
|
terminationGracePeriodSeconds: 0
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: node-role.kubernetes.io/master
|
||||||
|
operator: Equal
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: node-role.kubernetes.io/control-plane
|
||||||
|
operator: Equal
|
||||||
|
volumes:
|
||||||
|
- name: webhook-cert
|
||||||
|
secret:
|
||||||
|
secretName: ingress-nginx-admission
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-create
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-create
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- create
|
||||||
|
- --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
|
||||||
|
- --namespace=$(POD_NAMESPACE)
|
||||||
|
- --secret-name=ingress-nginx-admission
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231226-1a7112e06@sha256:25d6a5f11211cc5c3f9f2bf552b585374af287b4debf693cacbe2da47daa5084
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: create
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 65532
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
serviceAccountName: ingress-nginx-admission
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-patch
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-patch
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- patch
|
||||||
|
- --webhook-name=ingress-nginx-admission
|
||||||
|
- --namespace=$(POD_NAMESPACE)
|
||||||
|
- --patch-mutating=false
|
||||||
|
- --secret-name=ingress-nginx-admission
|
||||||
|
- --patch-failure-policy=Fail
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231226-1a7112e06@sha256:25d6a5f11211cc5c3f9f2bf552b585374af287b4debf693cacbe2da47daa5084
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: patch
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 65532
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
serviceAccountName: ingress-nginx-admission
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: IngressClass
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
controller: k8s.io/ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
webhooks:
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: ingress-nginx-controller-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
path: /networking/v1/ingresses
|
||||||
|
failurePolicy: Fail
|
||||||
|
matchPolicy: Equivalent
|
||||||
|
name: validate.nginx.ingress.kubernetes.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
sideEffects: None
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Container Registry Stack
|
||||||
|
|
||||||
|
Host a container image registry
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
version: "1.0"
|
||||||
|
name: container-registry
|
||||||
|
description: "Container registry stack"
|
||||||
|
pods:
|
||||||
|
- container-registry
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# mars
|
||||||
|
|
||||||
|
On a fresh Digital Ocean droplet with Ubuntu:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/cerc-io/stack-orchestrator
|
||||||
|
cd stack-orchestrator
|
||||||
|
./scripts/quick-install-linux.sh
|
||||||
|
```
|
||||||
|
Read and follow the instructions output from the above output to complete installation, then:
|
||||||
|
|
||||||
|
```
|
||||||
|
laconic-so --stack mars-v2 setup-repositories
|
||||||
|
laconic-so --stack mars-v2 build-containers
|
||||||
|
laconic-so --stack mars-v2 deploy up
|
||||||
|
```
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
version: "0.1"
|
||||||
|
name: mars-v2
|
||||||
|
repos:
|
||||||
|
- github.com/mars-protocol/mars-v2-frontend
|
||||||
|
containers:
|
||||||
|
- cerc/mars-v2
|
||||||
|
pods:
|
||||||
|
- mars-v2
|
||||||
@@ -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
|
||||||
@@ -27,6 +27,7 @@ from stack_orchestrator.opts import opts
|
|||||||
from stack_orchestrator.util import (get_stack_file_path, get_parsed_deployment_spec, get_parsed_stack_config,
|
from stack_orchestrator.util import (get_stack_file_path, get_parsed_deployment_spec, get_parsed_stack_config,
|
||||||
global_options, get_yaml, get_pod_list, get_pod_file_path, pod_has_scripts,
|
global_options, get_yaml, get_pod_list, get_pod_file_path, pod_has_scripts,
|
||||||
get_pod_script_paths, get_plugin_code_paths, error_exit, env_var_map_from_file)
|
get_pod_script_paths, get_plugin_code_paths, error_exit, env_var_map_from_file)
|
||||||
|
from stack_orchestrator.deploy.spec import Spec
|
||||||
from stack_orchestrator.deploy.deploy_types import LaconicStackSetupCommand
|
from stack_orchestrator.deploy.deploy_types import LaconicStackSetupCommand
|
||||||
from stack_orchestrator.deploy.deployer_factory import getDeployerConfigGenerator
|
from stack_orchestrator.deploy.deployer_factory import getDeployerConfigGenerator
|
||||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||||
@@ -111,6 +112,7 @@ def _create_bind_dir_if_relative(volume, path_string, compose_dir):
|
|||||||
|
|
||||||
# See: https://stackoverflow.com/questions/45699189/editing-docker-compose-yml-with-pyyaml
|
# See: https://stackoverflow.com/questions/45699189/editing-docker-compose-yml-with-pyyaml
|
||||||
def _fixup_pod_file(pod, spec, compose_dir):
|
def _fixup_pod_file(pod, spec, compose_dir):
|
||||||
|
deployment_type = spec[constants.deploy_to_key]
|
||||||
# Fix up volumes
|
# Fix up volumes
|
||||||
if "volumes" in spec:
|
if "volumes" in spec:
|
||||||
spec_volumes = spec["volumes"]
|
spec_volumes = spec["volumes"]
|
||||||
@@ -119,9 +121,13 @@ def _fixup_pod_file(pod, spec, compose_dir):
|
|||||||
for volume in pod_volumes.keys():
|
for volume in pod_volumes.keys():
|
||||||
if volume in spec_volumes:
|
if volume in spec_volumes:
|
||||||
volume_spec = spec_volumes[volume]
|
volume_spec = spec_volumes[volume]
|
||||||
|
if volume_spec:
|
||||||
volume_spec_fixedup = volume_spec if Path(volume_spec).is_absolute() else f".{volume_spec}"
|
volume_spec_fixedup = volume_spec if Path(volume_spec).is_absolute() else f".{volume_spec}"
|
||||||
_create_bind_dir_if_relative(volume, volume_spec, compose_dir)
|
_create_bind_dir_if_relative(volume, volume_spec, compose_dir)
|
||||||
new_volume_spec = {"driver": "local",
|
# this is Docker specific
|
||||||
|
if spec.is_docker_deployment():
|
||||||
|
new_volume_spec = {
|
||||||
|
"driver": "local",
|
||||||
"driver_opts": {
|
"driver_opts": {
|
||||||
"type": "none",
|
"type": "none",
|
||||||
"device": volume_spec_fixedup,
|
"device": volume_spec_fixedup,
|
||||||
@@ -131,15 +137,18 @@ def _fixup_pod_file(pod, spec, compose_dir):
|
|||||||
pod["volumes"][volume] = new_volume_spec
|
pod["volumes"][volume] = new_volume_spec
|
||||||
|
|
||||||
# Fix up configmaps
|
# Fix up configmaps
|
||||||
if "configmaps" in spec:
|
if constants.configmaps_key in spec:
|
||||||
spec_cfgmaps = spec["configmaps"]
|
if spec.is_kubernetes_deployment():
|
||||||
|
spec_cfgmaps = spec[constants.configmaps_key]
|
||||||
if "volumes" in pod:
|
if "volumes" in pod:
|
||||||
pod_volumes = pod["volumes"]
|
pod_volumes = pod[constants.volumes_key]
|
||||||
for volume in pod_volumes.keys():
|
for volume in pod_volumes.keys():
|
||||||
if volume in spec_cfgmaps:
|
if volume in spec_cfgmaps:
|
||||||
volume_cfg = spec_cfgmaps[volume]
|
volume_cfg = spec_cfgmaps[volume]
|
||||||
# Just make the dir (if necessary)
|
# Just make the dir (if necessary)
|
||||||
_create_bind_dir_if_relative(volume, volume_cfg, compose_dir)
|
_create_bind_dir_if_relative(volume, volume_cfg, compose_dir)
|
||||||
|
else:
|
||||||
|
print(f"Warning: ConfigMaps not supported for {deployment_type}")
|
||||||
|
|
||||||
# Fix up ports
|
# Fix up ports
|
||||||
if "network" in spec and "ports" in spec["network"]:
|
if "network" in spec and "ports" in spec["network"]:
|
||||||
@@ -323,7 +332,7 @@ def init_operation(deploy_command_context, stack, deployer_type, config,
|
|||||||
if image_registry is None:
|
if image_registry is None:
|
||||||
error_exit("--image-registry must be supplied with --deploy-to k8s")
|
error_exit("--image-registry must be supplied with --deploy-to k8s")
|
||||||
spec_file_content.update({constants.kube_config_key: kube_config})
|
spec_file_content.update({constants.kube_config_key: kube_config})
|
||||||
spec_file_content.update({constants.image_resigtry_key: image_registry})
|
spec_file_content.update({constants.image_registry_key: image_registry})
|
||||||
else:
|
else:
|
||||||
# Check for --kube-config supplied for non-relevant deployer types
|
# Check for --kube-config supplied for non-relevant deployer types
|
||||||
if kube_config is not None:
|
if kube_config is not None:
|
||||||
@@ -358,10 +367,16 @@ def init_operation(deploy_command_context, stack, deployer_type, config,
|
|||||||
volume_descriptors = {}
|
volume_descriptors = {}
|
||||||
configmap_descriptors = {}
|
configmap_descriptors = {}
|
||||||
for named_volume in named_volumes["rw"]:
|
for named_volume in named_volumes["rw"]:
|
||||||
|
if "k8s" in deployer_type:
|
||||||
|
volume_descriptors[named_volume] = None
|
||||||
|
else:
|
||||||
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
for named_volume in named_volumes["ro"]:
|
for named_volume in named_volumes["ro"]:
|
||||||
if "k8s" in deployer_type and "config" in named_volume:
|
if "k8s" in deployer_type:
|
||||||
configmap_descriptors[named_volume] = f"./data/{named_volume}"
|
if "config" in named_volume:
|
||||||
|
configmap_descriptors[named_volume] = f"./configmaps/{named_volume}"
|
||||||
|
else:
|
||||||
|
volume_descriptors[named_volume] = None
|
||||||
else:
|
else:
|
||||||
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
if volume_descriptors:
|
if volume_descriptors:
|
||||||
@@ -406,6 +421,17 @@ def _create_deployment_file(deployment_dir: Path):
|
|||||||
output_file.write(f"{constants.cluster_id_key}: {cluster}\n")
|
output_file.write(f"{constants.cluster_id_key}: {cluster}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def _check_volume_definitions(spec):
|
||||||
|
if spec.is_kubernetes_deployment():
|
||||||
|
for volume_name, volume_path in spec.get_volumes().items():
|
||||||
|
if volume_path:
|
||||||
|
if not os.path.isabs(volume_path):
|
||||||
|
raise Exception(
|
||||||
|
f"Relative path {volume_path} for volume {volume_name} not "
|
||||||
|
f"supported for deployment type {spec.get_deployment_type()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("--spec-file", required=True, help="Spec file to use to create this deployment")
|
@click.option("--spec-file", required=True, help="Spec file to use to create this deployment")
|
||||||
@click.option("--deployment-dir", help="Create deployment files in this directory")
|
@click.option("--deployment-dir", help="Create deployment files in this directory")
|
||||||
@@ -421,7 +447,8 @@ def create(ctx, spec_file, deployment_dir, network_dir, initial_peers):
|
|||||||
# The init command's implementation is in a separate function so that we can
|
# The init command's implementation is in a separate function so that we can
|
||||||
# call it from other commands, bypassing the click decoration stuff
|
# call it from other commands, bypassing the click decoration stuff
|
||||||
def create_operation(deployment_command_context, spec_file, deployment_dir, network_dir, initial_peers):
|
def create_operation(deployment_command_context, spec_file, deployment_dir, network_dir, initial_peers):
|
||||||
parsed_spec = get_parsed_deployment_spec(spec_file)
|
parsed_spec = Spec(os.path.abspath(spec_file), get_parsed_deployment_spec(spec_file))
|
||||||
|
_check_volume_definitions(parsed_spec)
|
||||||
stack_name = parsed_spec["stack"]
|
stack_name = parsed_spec["stack"]
|
||||||
deployment_type = parsed_spec[constants.deploy_to_key]
|
deployment_type = parsed_spec[constants.deploy_to_key]
|
||||||
stack_file = get_stack_file_path(stack_name)
|
stack_file = get_stack_file_path(stack_name)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ def push_images_operation(command_context: DeployCommandContext, deployment_cont
|
|||||||
cluster_context = command_context.cluster_context
|
cluster_context = command_context.cluster_context
|
||||||
images: Set[str] = images_for_deployment(cluster_context.compose_files)
|
images: Set[str] = images_for_deployment(cluster_context.compose_files)
|
||||||
# Tag the images for the remote repo
|
# Tag the images for the remote repo
|
||||||
remote_repo_url = deployment_context.spec.obj[constants.image_resigtry_key]
|
remote_repo_url = deployment_context.spec.obj[constants.image_registry_key]
|
||||||
docker = DockerClient()
|
docker = DockerClient()
|
||||||
for image in images:
|
for image in images:
|
||||||
if _image_needs_pushed(image):
|
if _image_needs_pushed(image):
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from typing import Any, List, Set
|
|||||||
from stack_orchestrator.opts import opts
|
from stack_orchestrator.opts import opts
|
||||||
from stack_orchestrator.util import env_var_map_from_file
|
from stack_orchestrator.util import env_var_map_from_file
|
||||||
from stack_orchestrator.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files
|
from stack_orchestrator.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files
|
||||||
from stack_orchestrator.deploy.k8s.helpers import get_node_pv_mount_path
|
from stack_orchestrator.deploy.k8s.helpers import get_kind_pv_bind_mount_path
|
||||||
from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map, envs_from_compose_file, merge_envs
|
from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map, envs_from_compose_file, merge_envs
|
||||||
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment
|
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment
|
||||||
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
||||||
@@ -78,7 +78,30 @@ class ClusterInfo:
|
|||||||
if (opts.o.debug):
|
if (opts.o.debug):
|
||||||
print(f"Env vars: {self.environment_variables.map}")
|
print(f"Env vars: {self.environment_variables.map}")
|
||||||
|
|
||||||
def get_ingress(self):
|
def get_nodeport(self):
|
||||||
|
for pod_name in self.parsed_pod_yaml_map:
|
||||||
|
pod = self.parsed_pod_yaml_map[pod_name]
|
||||||
|
services = pod["services"]
|
||||||
|
for service_name in services:
|
||||||
|
service_info = services[service_name]
|
||||||
|
if "ports" in service_info:
|
||||||
|
port = int(service_info["ports"][0])
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"service port: {port}")
|
||||||
|
service = client.V1Service(
|
||||||
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-nodeport"),
|
||||||
|
spec=client.V1ServiceSpec(
|
||||||
|
type="NodePort",
|
||||||
|
ports=[client.V1ServicePort(
|
||||||
|
port=port,
|
||||||
|
target_port=port
|
||||||
|
)],
|
||||||
|
selector={"app": self.app_name}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return service
|
||||||
|
|
||||||
|
def get_ingress(self, use_tls=False):
|
||||||
# No ingress for a deployment that has no http-proxy defined, for now
|
# No ingress for a deployment that has no http-proxy defined, for now
|
||||||
http_proxy_info_list = self.spec.get_http_proxy()
|
http_proxy_info_list = self.spec.get_http_proxy()
|
||||||
ingress = None
|
ingress = None
|
||||||
@@ -93,7 +116,7 @@ class ClusterInfo:
|
|||||||
tls = [client.V1IngressTLS(
|
tls = [client.V1IngressTLS(
|
||||||
hosts=[host_name],
|
hosts=[host_name],
|
||||||
secret_name=f"{self.app_name}-tls"
|
secret_name=f"{self.app_name}-tls"
|
||||||
)]
|
)] if use_tls else None
|
||||||
paths = []
|
paths = []
|
||||||
for route in http_proxy_info["routes"]:
|
for route in http_proxy_info["routes"]:
|
||||||
path = route["path"]
|
path = route["path"]
|
||||||
@@ -171,21 +194,33 @@ class ClusterInfo:
|
|||||||
print(f"Spec Volumes: {spec_volumes}")
|
print(f"Spec Volumes: {spec_volumes}")
|
||||||
print(f"Named Volumes: {named_volumes}")
|
print(f"Named Volumes: {named_volumes}")
|
||||||
print(f"Resources: {resources}")
|
print(f"Resources: {resources}")
|
||||||
for volume_name in spec_volumes:
|
for volume_name, volume_path in spec_volumes.items():
|
||||||
if volume_name not in named_volumes:
|
if volume_name not in named_volumes:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"{volume_name} not in pod files")
|
print(f"{volume_name} not in pod files")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
"app": self.app_name,
|
||||||
|
"volume-label": f"{self.app_name}-{volume_name}"
|
||||||
|
}
|
||||||
|
if volume_path:
|
||||||
|
storage_class_name = "manual"
|
||||||
|
k8s_volume_name = f"{self.app_name}-{volume_name}"
|
||||||
|
else:
|
||||||
|
# These will be auto-assigned.
|
||||||
|
storage_class_name = None
|
||||||
|
k8s_volume_name = None
|
||||||
|
|
||||||
spec = client.V1PersistentVolumeClaimSpec(
|
spec = client.V1PersistentVolumeClaimSpec(
|
||||||
access_modes=["ReadWriteOnce"],
|
access_modes=["ReadWriteOnce"],
|
||||||
storage_class_name="manual",
|
storage_class_name=storage_class_name,
|
||||||
resources=to_k8s_resource_requirements(resources),
|
resources=to_k8s_resource_requirements(resources),
|
||||||
volume_name=f"{self.app_name}-{volume_name}"
|
volume_name=k8s_volume_name
|
||||||
)
|
)
|
||||||
pvc = client.V1PersistentVolumeClaim(
|
pvc = client.V1PersistentVolumeClaim(
|
||||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}", labels=labels),
|
||||||
labels={"volume-label": f"{self.app_name}-{volume_name}"}),
|
spec=spec
|
||||||
spec=spec,
|
|
||||||
)
|
)
|
||||||
result.append(pvc)
|
result.append(pvc)
|
||||||
return result
|
return result
|
||||||
@@ -226,16 +261,32 @@ class ClusterInfo:
|
|||||||
resources = self.spec.get_volume_resources()
|
resources = self.spec.get_volume_resources()
|
||||||
if not resources:
|
if not resources:
|
||||||
resources = DEFAULT_VOLUME_RESOURCES
|
resources = DEFAULT_VOLUME_RESOURCES
|
||||||
for volume_name in spec_volumes:
|
for volume_name, volume_path in spec_volumes.items():
|
||||||
|
# We only need to create a volume if it is fully qualified HostPath.
|
||||||
|
# Otherwise, we create the PVC and expect the node to allocate the volume for us.
|
||||||
|
if not volume_path:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"{volume_name} does not require an explicit PersistentVolume, since it is not a bind-mount.")
|
||||||
|
continue
|
||||||
|
|
||||||
if volume_name not in named_volumes:
|
if volume_name not in named_volumes:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"{volume_name} not in pod files")
|
print(f"{volume_name} not in pod files")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not os.path.isabs(volume_path):
|
||||||
|
print(f"WARNING: {volume_name}:{volume_path} is not absolute, cannot bind volume.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.spec.is_kind_deployment():
|
||||||
|
host_path = client.V1HostPathVolumeSource(path=get_kind_pv_bind_mount_path(volume_name))
|
||||||
|
else:
|
||||||
|
host_path = client.V1HostPathVolumeSource(path=volume_path)
|
||||||
spec = client.V1PersistentVolumeSpec(
|
spec = client.V1PersistentVolumeSpec(
|
||||||
storage_class_name="manual",
|
storage_class_name="manual",
|
||||||
access_modes=["ReadWriteOnce"],
|
access_modes=["ReadWriteOnce"],
|
||||||
capacity=to_k8s_resource_requirements(resources).requests,
|
capacity=to_k8s_resource_requirements(resources).requests,
|
||||||
host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name))
|
host_path=host_path
|
||||||
)
|
)
|
||||||
pv = client.V1PersistentVolume(
|
pv = client.V1PersistentVolume(
|
||||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
||||||
@@ -281,13 +332,37 @@ class ClusterInfo:
|
|||||||
env=envs,
|
env=envs,
|
||||||
ports=[client.V1ContainerPort(container_port=port)],
|
ports=[client.V1ContainerPort(container_port=port)],
|
||||||
volume_mounts=volume_mounts,
|
volume_mounts=volume_mounts,
|
||||||
|
security_context=client.V1SecurityContext(
|
||||||
|
privileged=self.spec.get_privileged(),
|
||||||
|
capabilities=client.V1Capabilities(
|
||||||
|
add=self.spec.get_capabilities()
|
||||||
|
) if self.spec.get_capabilities() else None
|
||||||
|
),
|
||||||
resources=to_k8s_resource_requirements(resources),
|
resources=to_k8s_resource_requirements(resources),
|
||||||
)
|
)
|
||||||
containers.append(container)
|
containers.append(container)
|
||||||
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name)
|
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name)
|
||||||
image_pull_secrets = [client.V1LocalObjectReference(name="laconic-registry")]
|
image_pull_secrets = [client.V1LocalObjectReference(name="laconic-registry")]
|
||||||
|
|
||||||
|
annotations = None
|
||||||
|
labels = {"app": self.app_name}
|
||||||
|
|
||||||
|
if self.spec.get_annotations():
|
||||||
|
annotations = {}
|
||||||
|
for key, value in self.spec.get_annotations().items():
|
||||||
|
for service_name in services:
|
||||||
|
annotations[key.replace("{name}", service_name)] = value
|
||||||
|
|
||||||
|
if self.spec.get_labels():
|
||||||
|
for key, value in self.spec.get_labels().items():
|
||||||
|
for service_name in services:
|
||||||
|
labels[key.replace("{name}", service_name)] = value
|
||||||
|
|
||||||
template = client.V1PodTemplateSpec(
|
template = client.V1PodTemplateSpec(
|
||||||
metadata=client.V1ObjectMeta(labels={"app": self.app_name}),
|
metadata=client.V1ObjectMeta(
|
||||||
|
annotations=annotations,
|
||||||
|
labels=labels
|
||||||
|
),
|
||||||
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
|
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
|
||||||
)
|
)
|
||||||
spec = client.V1DeploymentSpec(
|
spec = client.V1DeploymentSpec(
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ from kubernetes import client, config
|
|||||||
from stack_orchestrator import constants
|
from stack_orchestrator import constants
|
||||||
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
|
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 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 install_ingress_for_kind, wait_for_ingress_in_kind
|
||||||
|
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.deploy.k8s.cluster_info import ClusterInfo
|
||||||
from stack_orchestrator.opts import opts
|
from stack_orchestrator.opts import opts
|
||||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||||
@@ -88,6 +90,16 @@ class K8sDeployer(Deployer):
|
|||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this pv: {pv}")
|
print(f"Sending this pv: {pv}")
|
||||||
if not opts.o.dry_run:
|
if not opts.o.dry_run:
|
||||||
|
try:
|
||||||
|
pv_resp = self.core_api.read_persistent_volume(name=pv.metadata.name)
|
||||||
|
if pv_resp:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVs already present:")
|
||||||
|
print(f"{pv_resp}")
|
||||||
|
continue
|
||||||
|
except: # noqa: E722
|
||||||
|
pass
|
||||||
|
|
||||||
pv_resp = self.core_api.create_persistent_volume(body=pv)
|
pv_resp = self.core_api.create_persistent_volume(body=pv)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("PVs created:")
|
print("PVs created:")
|
||||||
@@ -100,6 +112,17 @@ class K8sDeployer(Deployer):
|
|||||||
print(f"Sending this pvc: {pvc}")
|
print(f"Sending this pvc: {pvc}")
|
||||||
|
|
||||||
if not opts.o.dry_run:
|
if not opts.o.dry_run:
|
||||||
|
try:
|
||||||
|
pvc_resp = self.core_api.read_namespaced_persistent_volume_claim(
|
||||||
|
name=pvc.metadata.name, namespace=self.k8s_namespace)
|
||||||
|
if pvc_resp:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVCs already present:")
|
||||||
|
print(f"{pvc_resp}")
|
||||||
|
continue
|
||||||
|
except: # noqa: E722
|
||||||
|
pass
|
||||||
|
|
||||||
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("PVCs created:")
|
print("PVCs created:")
|
||||||
@@ -154,15 +177,20 @@ class K8sDeployer(Deployer):
|
|||||||
# Ensure the referenced containers are copied into kind
|
# Ensure the referenced containers are copied into kind
|
||||||
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
||||||
self.connect_api()
|
self.connect_api()
|
||||||
|
if self.is_kind():
|
||||||
|
# Now configure an ingress controller (not installed by default in kind)
|
||||||
|
install_ingress_for_kind()
|
||||||
|
# Wait for ingress to start (deployment provisioning will fail unless this is done)
|
||||||
|
wait_for_ingress_in_kind()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("Dry run mode enabled, skipping k8s API connect")
|
print("Dry run mode enabled, skipping k8s API connect")
|
||||||
|
|
||||||
self._create_volume_data()
|
self._create_volume_data()
|
||||||
self._create_deployment()
|
self._create_deployment()
|
||||||
|
|
||||||
if not self.is_kind():
|
# Note: at present we don't support tls for kind (and enabling tls causes errors)
|
||||||
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=not self.is_kind())
|
||||||
|
|
||||||
if ingress:
|
if ingress:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this ingress: {ingress}")
|
print(f"Sending this ingress: {ingress}")
|
||||||
@@ -178,9 +206,24 @@ class K8sDeployer(Deployer):
|
|||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("No ingress configured")
|
print("No ingress configured")
|
||||||
|
|
||||||
|
nodeport: client.V1Service = self.cluster_info.get_nodeport()
|
||||||
|
if nodeport:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Sending this nodeport: {nodeport}")
|
||||||
|
if not opts.o.dry_run:
|
||||||
|
nodeport_resp = self.core_api.create_namespaced_service(
|
||||||
|
namespace=self.k8s_namespace,
|
||||||
|
body=nodeport
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("NodePort created:")
|
||||||
|
print(f"{nodeport_resp}")
|
||||||
|
|
||||||
def down(self, timeout, volumes): # noqa: C901
|
def down(self, timeout, volumes): # noqa: C901
|
||||||
self.connect_api()
|
self.connect_api()
|
||||||
# Delete the k8s objects
|
# Delete the k8s objects
|
||||||
|
|
||||||
|
if volumes:
|
||||||
# Create the host-path-mounted PVs for this deployment
|
# Create the host-path-mounted PVs for this deployment
|
||||||
pvs = self.cluster_info.get_pvs()
|
pvs = self.cluster_info.get_pvs()
|
||||||
for pv in pvs:
|
for pv in pvs:
|
||||||
@@ -245,8 +288,7 @@ class K8sDeployer(Deployer):
|
|||||||
except client.exceptions.ApiException as e:
|
except client.exceptions.ApiException as e:
|
||||||
_check_delete_exception(e)
|
_check_delete_exception(e)
|
||||||
|
|
||||||
if not self.is_kind():
|
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=not self.is_kind())
|
||||||
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
|
||||||
if ingress:
|
if ingress:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Deleting this ingress: {ingress}")
|
print(f"Deleting this ingress: {ingress}")
|
||||||
@@ -260,6 +302,21 @@ class K8sDeployer(Deployer):
|
|||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("No ingress to delete")
|
print("No ingress to delete")
|
||||||
|
|
||||||
|
nodeport: client.V1Service = self.cluster_info.get_nodeport()
|
||||||
|
if nodeport:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Deleting this nodeport: {ingress}")
|
||||||
|
try:
|
||||||
|
self.core_api.delete_namespaced_service(
|
||||||
|
namespace=self.k8s_namespace,
|
||||||
|
name=nodeport.metadata.name
|
||||||
|
)
|
||||||
|
except client.exceptions.ApiException as e:
|
||||||
|
_check_delete_exception(e)
|
||||||
|
else:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("No nodeport to delete")
|
||||||
|
|
||||||
if self.is_kind():
|
if self.is_kind():
|
||||||
# Destroy the kind cluster
|
# Destroy the kind cluster
|
||||||
destroy_cluster(self.kind_cluster_name)
|
destroy_cluster(self.kind_cluster_name)
|
||||||
@@ -359,9 +416,15 @@ class K8sDeployer(Deployer):
|
|||||||
log_data = "******* Pods not running ********\n"
|
log_data = "******* Pods not running ********\n"
|
||||||
else:
|
else:
|
||||||
k8s_pod_name = pods[0]
|
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
|
# If the pod is not yet started, the logs request below will throw an exception
|
||||||
try:
|
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:
|
except client.exceptions.ApiException as e:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Error from read_namespaced_pod_log: {e}")
|
print(f"Error from read_namespaced_pod_log: {e}")
|
||||||
|
|||||||
@@ -13,13 +13,14 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from kubernetes import client
|
from kubernetes import client, utils, watch
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
from typing import Set, Mapping, List
|
from typing import Set, Mapping, List
|
||||||
|
|
||||||
|
from stack_orchestrator.util import get_k8s_dir, error_exit
|
||||||
from stack_orchestrator.opts import opts
|
from stack_orchestrator.opts import opts
|
||||||
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names
|
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names
|
||||||
from stack_orchestrator.deploy.deployer import DeployerException
|
from stack_orchestrator.deploy.deployer import DeployerException
|
||||||
@@ -44,6 +45,33 @@ def destroy_cluster(name: str):
|
|||||||
_run_command(f"kind delete cluster --name {name}")
|
_run_command(f"kind delete cluster --name {name}")
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_ingress_in_kind():
|
||||||
|
core_v1 = client.CoreV1Api()
|
||||||
|
for i in range(20):
|
||||||
|
warned_waiting = False
|
||||||
|
w = watch.Watch()
|
||||||
|
for event in w.stream(func=core_v1.list_namespaced_pod,
|
||||||
|
namespace="ingress-nginx",
|
||||||
|
label_selector="app.kubernetes.io/component=controller",
|
||||||
|
timeout_seconds=30):
|
||||||
|
if event['object'].status.container_statuses:
|
||||||
|
if event['object'].status.container_statuses[0].ready is True:
|
||||||
|
if warned_waiting:
|
||||||
|
print("Ingress controller is ready")
|
||||||
|
return
|
||||||
|
print("Waiting for ingress controller to become ready...")
|
||||||
|
warned_waiting = True
|
||||||
|
error_exit("ERROR: Timed out waiting for ingress to become ready")
|
||||||
|
|
||||||
|
|
||||||
|
def install_ingress_for_kind():
|
||||||
|
api_client = client.ApiClient()
|
||||||
|
ingress_install = os.path.abspath(get_k8s_dir().joinpath("components", "ingress", "ingress-nginx-kind-deploy.yaml"))
|
||||||
|
if opts.o.debug:
|
||||||
|
print("Installing nginx ingress controller in kind cluster")
|
||||||
|
utils.create_from_yaml(api_client, yaml_file=ingress_install)
|
||||||
|
|
||||||
|
|
||||||
def load_images_into_kind(kind_cluster_name: str, image_set: Set[str]):
|
def load_images_into_kind(kind_cluster_name: str, image_set: Set[str]):
|
||||||
for image in image_set:
|
for image in image_set:
|
||||||
result = _run_command(f"kind load docker-image {image} --name {kind_cluster_name}")
|
result = _run_command(f"kind load docker-image {image} --name {kind_cluster_name}")
|
||||||
@@ -62,6 +90,17 @@ def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str):
|
|||||||
return pods
|
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):
|
def log_stream_from_string(s: str):
|
||||||
# Note response has to be UTF-8 encoded because the caller expects to decode it
|
# Note response has to be UTF-8 encoded because the caller expects to decode it
|
||||||
yield ("ignore", s.encode())
|
yield ("ignore", s.encode())
|
||||||
@@ -81,7 +120,7 @@ def named_volumes_from_pod_files(parsed_pod_files):
|
|||||||
return named_volumes
|
return named_volumes
|
||||||
|
|
||||||
|
|
||||||
def get_node_pv_mount_path(volume_name: str):
|
def get_kind_pv_bind_mount_path(volume_name: str):
|
||||||
return f"/mnt/{volume_name}"
|
return f"/mnt/{volume_name}"
|
||||||
|
|
||||||
|
|
||||||
@@ -106,11 +145,14 @@ def volume_mounts_for_service(parsed_pod_files, service):
|
|||||||
mount_path = mount_split[1]
|
mount_path = mount_split[1]
|
||||||
mount_options = mount_split[2] if len(mount_split) == 3 else None
|
mount_options = mount_split[2] if len(mount_split) == 3 else None
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"volumne_name: {volume_name}")
|
print(f"volume_name: {volume_name}")
|
||||||
print(f"mount path: {mount_path}")
|
print(f"mount path: {mount_path}")
|
||||||
print(f"mount options: {mount_options}")
|
print(f"mount options: {mount_options}")
|
||||||
volume_device = client.V1VolumeMount(
|
volume_device = client.V1VolumeMount(
|
||||||
mount_path=mount_path, name=volume_name, read_only="ro" == mount_options)
|
mount_path=mount_path,
|
||||||
|
name=volume_name,
|
||||||
|
read_only="ro" == mount_options
|
||||||
|
)
|
||||||
result.append(volume_device)
|
result.append(volume_device)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -133,18 +175,8 @@ def volumes_for_pod_files(parsed_pod_files, spec, app_name):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_host_paths_for_volumes(parsed_pod_files):
|
def _get_host_paths_for_volumes(deployment_context):
|
||||||
result = {}
|
return deployment_context.spec.get_volumes()
|
||||||
for pod in parsed_pod_files:
|
|
||||||
parsed_pod_file = parsed_pod_files[pod]
|
|
||||||
if "volumes" in parsed_pod_file:
|
|
||||||
volumes = parsed_pod_file["volumes"]
|
|
||||||
for volume_name in volumes.keys():
|
|
||||||
volume_definition = volumes[volume_name]
|
|
||||||
if volume_definition and "driver_opts" in volume_definition:
|
|
||||||
host_path = volume_definition["driver_opts"]["device"]
|
|
||||||
result[volume_name] = host_path
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Path:
|
def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Path:
|
||||||
@@ -152,12 +184,12 @@ def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Pat
|
|||||||
return data_mount_path
|
return data_mount_path
|
||||||
else:
|
else:
|
||||||
# Python Path voodo that looks pretty odd:
|
# Python Path voodo that looks pretty odd:
|
||||||
return Path.cwd().joinpath(deployment_dir.joinpath("compose").joinpath(data_mount_path)).resolve()
|
return Path.cwd().joinpath(deployment_dir.joinpath(data_mount_path)).resolve()
|
||||||
|
|
||||||
|
|
||||||
def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
||||||
volume_definitions = []
|
volume_definitions = []
|
||||||
volume_host_path_map = _get_host_paths_for_volumes(parsed_pod_files)
|
volume_host_path_map = _get_host_paths_for_volumes(deployment_context)
|
||||||
# Note these paths are relative to the location of the pod files (at present)
|
# Note these paths are relative to the location of the pod files (at present)
|
||||||
# So we need to fix up to make them correct and absolute because kind assumes
|
# So we need to fix up to make them correct and absolute because kind assumes
|
||||||
# relative to the cwd.
|
# relative to the cwd.
|
||||||
@@ -177,13 +209,14 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
|||||||
volume_name = mount_split[0]
|
volume_name = mount_split[0]
|
||||||
mount_path = mount_split[1]
|
mount_path = mount_split[1]
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"volumne_name: {volume_name}")
|
print(f"volume_name: {volume_name}")
|
||||||
print(f"map: {volume_host_path_map}")
|
print(f"map: {volume_host_path_map}")
|
||||||
print(f"mount path: {mount_path}")
|
print(f"mount path: {mount_path}")
|
||||||
if volume_name not in deployment_context.spec.get_configmaps():
|
if volume_name not in deployment_context.spec.get_configmaps():
|
||||||
|
if volume_host_path_map[volume_name]:
|
||||||
volume_definitions.append(
|
volume_definitions.append(
|
||||||
f" - hostPath: {_make_absolute_host_path(volume_host_path_map[volume_name], deployment_dir)}\n"
|
f" - hostPath: {_make_absolute_host_path(volume_host_path_map[volume_name], deployment_dir)}\n"
|
||||||
f" containerPath: {get_node_pv_mount_path(volume_name)}\n"
|
f" containerPath: {get_kind_pv_bind_mount_path(volume_name)}\n"
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
"" if len(volume_definitions) == 0 else (
|
"" if len(volume_definitions) == 0 else (
|
||||||
@@ -193,7 +226,8 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _generate_kind_port_mappings(parsed_pod_files):
|
# TODO: decide if we need this functionality
|
||||||
|
def _generate_kind_port_mappings_from_services(parsed_pod_files):
|
||||||
port_definitions = []
|
port_definitions = []
|
||||||
for pod in parsed_pod_files:
|
for pod in parsed_pod_files:
|
||||||
parsed_pod_file = parsed_pod_files[pod]
|
parsed_pod_file = parsed_pod_files[pod]
|
||||||
@@ -215,6 +249,19 @@ def _generate_kind_port_mappings(parsed_pod_files):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_kind_port_mappings(parsed_pod_files):
|
||||||
|
port_definitions = []
|
||||||
|
# For now we just map port 80 for the nginx ingress controller we install in kind
|
||||||
|
port_string = "80"
|
||||||
|
port_definitions.append(f" - containerPort: {port_string}\n hostPort: {port_string}\n")
|
||||||
|
return (
|
||||||
|
"" if len(port_definitions) == 0 else (
|
||||||
|
" extraPortMappings:\n"
|
||||||
|
f"{''.join(port_definitions)}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Note: this makes any duplicate definition in b overwrite a
|
# Note: this makes any duplicate definition in b overwrite a
|
||||||
def merge_envs(a: Mapping[str, str], b: Mapping[str, str]) -> Mapping[str, str]:
|
def merge_envs(a: Mapping[str, str], b: Mapping[str, str]) -> Mapping[str, str]:
|
||||||
result = {**a, **b}
|
result = {**a, **b}
|
||||||
@@ -279,6 +326,12 @@ def generate_kind_config(deployment_dir: Path, deployment_context):
|
|||||||
"apiVersion: kind.x-k8s.io/v1alpha4\n"
|
"apiVersion: kind.x-k8s.io/v1alpha4\n"
|
||||||
"nodes:\n"
|
"nodes:\n"
|
||||||
"- role: control-plane\n"
|
"- role: control-plane\n"
|
||||||
|
" kubeadmConfigPatches:\n"
|
||||||
|
" - |\n"
|
||||||
|
" kind: InitConfiguration\n"
|
||||||
|
" nodeRegistration:\n"
|
||||||
|
" kubeletExtraArgs:\n"
|
||||||
|
" node-labels: \"ingress-ready=true\"\n"
|
||||||
f"{port_mappings_yml}\n"
|
f"{port_mappings_yml}\n"
|
||||||
f"{mounts_yml}\n"
|
f"{mounts_yml}\n"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -72,8 +72,18 @@ class Spec:
|
|||||||
obj: typing.Any
|
obj: typing.Any
|
||||||
file_path: Path
|
file_path: Path
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, file_path: Path = None, obj={}) -> None:
|
||||||
pass
|
self.file_path = file_path
|
||||||
|
self.obj = obj
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.obj[item]
|
||||||
|
|
||||||
|
def __contains__(self, item):
|
||||||
|
return item in self.obj
|
||||||
|
|
||||||
|
def get(self, item, default=None):
|
||||||
|
return self.obj.get(item, default)
|
||||||
|
|
||||||
def init_from_file(self, file_path: Path):
|
def init_from_file(self, file_path: Path):
|
||||||
with file_path:
|
with file_path:
|
||||||
@@ -81,8 +91,8 @@ class Spec:
|
|||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
|
||||||
def get_image_registry(self):
|
def get_image_registry(self):
|
||||||
return (self.obj[constants.image_resigtry_key]
|
return (self.obj[constants.image_registry_key]
|
||||||
if self.obj and constants.image_resigtry_key in self.obj
|
if self.obj and constants.image_registry_key in self.obj
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
def get_volumes(self):
|
def get_volumes(self):
|
||||||
@@ -106,3 +116,27 @@ class Spec:
|
|||||||
if self.obj and constants.network_key in self.obj
|
if self.obj and constants.network_key in self.obj
|
||||||
and constants.http_proxy_key in self.obj[constants.network_key]
|
and constants.http_proxy_key in self.obj[constants.network_key]
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
|
def get_annotations(self):
|
||||||
|
return self.obj.get("annotations", {})
|
||||||
|
|
||||||
|
def get_labels(self):
|
||||||
|
return self.obj.get("labels", {})
|
||||||
|
|
||||||
|
def get_privileged(self):
|
||||||
|
return "true" == str(self.obj.get("security", {}).get("privileged", "false")).lower()
|
||||||
|
|
||||||
|
def get_capabilities(self):
|
||||||
|
return self.obj.get("security", {}).get("capabilities", [])
|
||||||
|
|
||||||
|
def get_deployment_type(self):
|
||||||
|
return self.obj[constants.deploy_to_key]
|
||||||
|
|
||||||
|
def is_kubernetes_deployment(self):
|
||||||
|
return self.get_deployment_type() in [constants.k8s_kind_deploy_type, constants.k8s_deploy_type]
|
||||||
|
|
||||||
|
def is_kind_deployment(self):
|
||||||
|
return self.get_deployment_type() in [constants.k8s_kind_deploy_type]
|
||||||
|
|
||||||
|
def is_docker_deployment(self):
|
||||||
|
return self.get_deployment_type() in [constants.compose_deploy_type]
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def _fixup_url_spec(spec_file_name: str, url: str):
|
|||||||
- host-name: {parsed_url.hostname}
|
- host-name: {parsed_url.hostname}
|
||||||
routes:
|
routes:
|
||||||
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
||||||
proxy-to: webapp:3000
|
proxy-to: webapp:80
|
||||||
'''
|
'''
|
||||||
spec_file_path = Path(spec_file_name)
|
spec_file_path = Path(spec_file_name)
|
||||||
with open(spec_file_path) as rfile:
|
with open(spec_file_path) as rfile:
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ def command(ctx, laconic_config, deployment_parent_dir,
|
|||||||
|
|
||||||
previous_requests = load_known_requests(state_file)
|
previous_requests = load_known_requests(state_file)
|
||||||
requests.sort(key=lambda r: r.createTime)
|
requests.sort(key=lambda r: r.createTime)
|
||||||
|
requests.reverse()
|
||||||
|
|
||||||
# Find deployments.
|
# Find deployments.
|
||||||
deployments = {}
|
deployments = {}
|
||||||
|
|||||||
@@ -355,11 +355,13 @@ def generate_hostname_for_app(app):
|
|||||||
|
|
||||||
def skip_by_tag(r, include_tags, exclude_tags):
|
def skip_by_tag(r, include_tags, exclude_tags):
|
||||||
for tag in exclude_tags:
|
for tag in exclude_tags:
|
||||||
if tag and r.attributes.tags and tag in r.attributes.tags:
|
if r.attributes.tags and tag in r.attributes.tags:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if include_tags:
|
||||||
for tag in include_tags:
|
for tag in include_tags:
|
||||||
if tag and (not r.attributes.tags or tag not in r.attributes.tags):
|
if r.attributes.tags and tag in r.attributes.tags:
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import importlib.resources
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import yaml
|
import yaml
|
||||||
from stack_orchestrator.constants import stack_file_name
|
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):
|
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")
|
error_exit(f"stack {stack} does not exist")
|
||||||
with stack_file_path:
|
with stack_file_path:
|
||||||
stack_config = yaml.safe_load(open(stack_file_path, "r"))
|
stack_config = yaml.safe_load(open(stack_file_path, "r"))
|
||||||
if "repos" not in stack_config:
|
if "repos" not in stack_config or stack_config["repos"] is None:
|
||||||
error_exit(f"stack {stack} does not define any repositories")
|
warn_exit(f"stack {stack} does not define any repositories")
|
||||||
else:
|
else:
|
||||||
repos_in_scope = stack_config["repos"]
|
repos_in_scope = stack_config["repos"]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ def get_config_file_dir():
|
|||||||
return source_config_dir
|
return source_config_dir
|
||||||
|
|
||||||
|
|
||||||
|
def get_k8s_dir():
|
||||||
|
data_dir = Path(__file__).absolute().parent.joinpath("data")
|
||||||
|
source_config_dir = data_dir.joinpath("k8s")
|
||||||
|
return source_config_dir
|
||||||
|
|
||||||
|
|
||||||
def get_parsed_deployment_spec(spec_file):
|
def get_parsed_deployment_spec(spec_file):
|
||||||
spec_file_path = Path(spec_file)
|
spec_file_path = Path(spec_file)
|
||||||
try:
|
try:
|
||||||
@@ -189,5 +195,10 @@ def error_exit(s):
|
|||||||
sys.exit(1)
|
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]:
|
def env_var_map_from_file(file: Path) -> Mapping[str, str]:
|
||||||
return dotenv_values(file)
|
return dotenv_values(file)
|
||||||
|
|||||||
Executable
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
# Dump environment variables for debugging
|
||||||
|
echo "Environment variables:"
|
||||||
|
env
|
||||||
|
fi
|
||||||
|
|
||||||
|
stack="container-registry"
|
||||||
|
|
||||||
|
# Helper functions: TODO move into a separate file
|
||||||
|
wait_for_pods_started () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
local ps_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir ps )
|
||||||
|
|
||||||
|
if [[ "$ps_output" == *"Running containers:"* ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for pods to start: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_log_output () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
|
||||||
|
local log_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
|
||||||
|
if [[ ! -z "$log_output" ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for pods log content: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delete_cluster_exit () {
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: eventually this test should be folded into ../deploy/
|
||||||
|
# but keeping it separate for now for convenience
|
||||||
|
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||||
|
# Set a non-default repo dir
|
||||||
|
export CERC_REPO_BASE_DIR=~/stack-orchestrator-test/repo-base-dir
|
||||||
|
echo "Testing this package: $TEST_TARGET_SO"
|
||||||
|
echo "Test version command"
|
||||||
|
reported_version_string=$( $TEST_TARGET_SO version )
|
||||||
|
echo "Version reported is: ${reported_version_string}"
|
||||||
|
echo "Cloning repositories into: $CERC_REPO_BASE_DIR"
|
||||||
|
rm -rf $CERC_REPO_BASE_DIR
|
||||||
|
mkdir -p $CERC_REPO_BASE_DIR
|
||||||
|
$TEST_TARGET_SO --stack ${stack} setup-repositories
|
||||||
|
$TEST_TARGET_SO --stack ${stack} build-containers
|
||||||
|
# Test basic stack-orchestrator deploy to k8s
|
||||||
|
test_deployment_dir=$CERC_REPO_BASE_DIR/${stack}-deployment-dir
|
||||||
|
test_deployment_spec=$CERC_REPO_BASE_DIR/${stack}-deployment-spec.yml
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy --deploy-to k8s-kind init --output $test_deployment_spec --config CERC_TEST_PARAM_1=PASSED
|
||||||
|
# Check the file now exists
|
||||||
|
if [ ! -f "$test_deployment_spec" ]; then
|
||||||
|
echo "deploy init test: spec file not present"
|
||||||
|
echo "deploy init test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy init test: passed"
|
||||||
|
|
||||||
|
# Switch to a full path for bind mount.
|
||||||
|
volume_name="registry-data"
|
||||||
|
sed -i "s|^\(\s*${volume_name}:$\)$|\1 ${test_deployment_dir}/data/${volume_name}|" $test_deployment_spec
|
||||||
|
|
||||||
|
# Add ingress config to the spec file
|
||||||
|
ed $test_deployment_spec <<IngressSpec
|
||||||
|
/network:/
|
||||||
|
a
|
||||||
|
http-proxy:
|
||||||
|
- host-name: localhost
|
||||||
|
routes:
|
||||||
|
- path: /
|
||||||
|
proxy-to: registry:5000
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
IngressSpec
|
||||||
|
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
||||||
|
# Check the deployment dir exists
|
||||||
|
if [ ! -d "$test_deployment_dir" ]; then
|
||||||
|
echo "deploy create test: deployment directory not present"
|
||||||
|
echo "deploy create test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy create test: passed"
|
||||||
|
|
||||||
|
# Note: this isn't strictly necessary, except we end up trying to push the image into
|
||||||
|
# the kind cluster then fails because it can't be found locally
|
||||||
|
docker pull registry:2.8
|
||||||
|
|
||||||
|
# Try to start the deployment
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
|
wait_for_pods_started
|
||||||
|
# Check logs command works
|
||||||
|
wait_for_log_output
|
||||||
|
sleep 1
|
||||||
|
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_3" == *"listening on"* ]]; then
|
||||||
|
echo "deployment logs test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment logs test: FAILED"
|
||||||
|
echo $log_output_3
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that we can use the registry
|
||||||
|
# Note: since this pulls from the DockerCo registry without auth it's possible it'll run into rate limiting issues
|
||||||
|
docker pull hello-world
|
||||||
|
docker tag hello-world localhost:80/hello-world
|
||||||
|
docker push localhost:80/hello-world
|
||||||
|
# Then do a quick check that we actually pushed something there
|
||||||
|
# See: https://stackoverflow.com/questions/31251356/how-to-get-a-list-of-images-on-docker-registry-v2
|
||||||
|
registry_response=$(curl -s -X GET http://localhost:80/v2/_catalog)
|
||||||
|
if [[ "$registry_response" == *"{\"repositories\":[\"hello-world\"]}"* ]]; then
|
||||||
|
echo "registry content test: passed"
|
||||||
|
else
|
||||||
|
echo "registry content test: FAILED"
|
||||||
|
echo $registry_response
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop and clean up
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
echo "Test passed"
|
||||||
Executable
+131
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
# Dump environment variables for debugging
|
||||||
|
echo "Environment variables:"
|
||||||
|
env
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" == "from-path" ]; then
|
||||||
|
TEST_TARGET_SO="laconic-so"
|
||||||
|
else
|
||||||
|
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||||
|
fi
|
||||||
|
|
||||||
|
stack="test-database"
|
||||||
|
spec_file=${stack}-spec.yml
|
||||||
|
deployment_dir=${stack}-deployment
|
||||||
|
|
||||||
|
# Helper functions: TODO move into a separate file
|
||||||
|
wait_for_pods_started () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
local ps_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir ps )
|
||||||
|
|
||||||
|
if [[ "$ps_output" == *"Running containers:"* ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for pods to start: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_test_complete () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
|
||||||
|
local log_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
|
||||||
|
if [[ "${log_output}" == *"Database test client: test complete"* ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for test complete: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delete_cluster_exit () {
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set a non-default repo dir
|
||||||
|
export CERC_REPO_BASE_DIR=~/stack-orchestrator-test/repo-base-dir
|
||||||
|
echo "Testing this package: $TEST_TARGET_SO"
|
||||||
|
echo "Test version command"
|
||||||
|
reported_version_string=$( $TEST_TARGET_SO version )
|
||||||
|
echo "Version reported is: ${reported_version_string}"
|
||||||
|
echo "Cloning repositories into: $CERC_REPO_BASE_DIR"
|
||||||
|
rm -rf $CERC_REPO_BASE_DIR
|
||||||
|
mkdir -p $CERC_REPO_BASE_DIR
|
||||||
|
$TEST_TARGET_SO --stack ${stack} setup-repositories
|
||||||
|
$TEST_TARGET_SO --stack ${stack} build-containers
|
||||||
|
# Test basic stack-orchestrator deploy to k8s
|
||||||
|
test_deployment_dir=$CERC_REPO_BASE_DIR/${deployment_dir}
|
||||||
|
test_deployment_spec=$CERC_REPO_BASE_DIR/${spec_file}
|
||||||
|
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy --deploy-to k8s-kind init --output $test_deployment_spec
|
||||||
|
# Check the file now exists
|
||||||
|
if [ ! -f "$test_deployment_spec" ]; then
|
||||||
|
echo "deploy init test: spec file not present"
|
||||||
|
echo "deploy init test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy init test: passed"
|
||||||
|
|
||||||
|
# Switch to a full path for the data dir so it gets provisioned as a host bind mounted volume and preserved beyond cluster lifetime
|
||||||
|
sed -i "s|^\(\s*db-data:$\)$|\1 ${test_deployment_dir}/data/db-data|" $test_deployment_spec
|
||||||
|
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
||||||
|
# Check the deployment dir exists
|
||||||
|
if [ ! -d "$test_deployment_dir" ]; then
|
||||||
|
echo "deploy create test: deployment directory not present"
|
||||||
|
echo "deploy create test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy create test: passed"
|
||||||
|
|
||||||
|
# Try to start the deployment
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
|
wait_for_pods_started
|
||||||
|
# Check logs command works
|
||||||
|
wait_for_test_complete
|
||||||
|
log_output_1=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_1" == *"Database test client: test data does not exist"* ]]; then
|
||||||
|
echo "Create database content test: passed"
|
||||||
|
else
|
||||||
|
echo "Create database content test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop then start again and check the volume was preserved
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
||||||
|
# Sleep a bit just in case
|
||||||
|
sleep 20
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
|
wait_for_pods_started
|
||||||
|
wait_for_test_complete
|
||||||
|
|
||||||
|
log_output_2=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_2" == *"Database test client: test data already exists"* ]]; then
|
||||||
|
echo "Retain database content test: passed"
|
||||||
|
else
|
||||||
|
echo "Retain database content test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop and clean up
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
echo "Test passed"
|
||||||
@@ -63,7 +63,7 @@ $TEST_TARGET_SO --stack test deploy down
|
|||||||
# The next time we bring the container up the volume will be old (from the previous run above)
|
# The next time we bring the container up the volume will be old (from the previous run above)
|
||||||
$TEST_TARGET_SO --stack test deploy up
|
$TEST_TARGET_SO --stack test deploy up
|
||||||
log_output_1=$( $TEST_TARGET_SO --stack test deploy logs )
|
log_output_1=$( $TEST_TARGET_SO --stack test deploy logs )
|
||||||
if [[ "$log_output_1" == *"Filesystem is old"* ]]; then
|
if [[ "$log_output_1" == *"filesystem is old"* ]]; then
|
||||||
echo "Retain volumes test: passed"
|
echo "Retain volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Retain volumes test: FAILED"
|
echo "Retain volumes test: FAILED"
|
||||||
@@ -73,7 +73,7 @@ $TEST_TARGET_SO --stack test deploy down --delete-volumes
|
|||||||
# Now when we bring the container up the volume will be new again
|
# Now when we bring the container up the volume will be new again
|
||||||
$TEST_TARGET_SO --stack test deploy up
|
$TEST_TARGET_SO --stack test deploy up
|
||||||
log_output_2=$( $TEST_TARGET_SO --stack test deploy logs )
|
log_output_2=$( $TEST_TARGET_SO --stack test deploy logs )
|
||||||
if [[ "$log_output_2" == *"Filesystem is fresh"* ]]; then
|
if [[ "$log_output_2" == *"filesystem is fresh"* ]]; then
|
||||||
echo "Delete volumes test: passed"
|
echo "Delete volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Delete volumes test: FAILED"
|
echo "Delete volumes test: FAILED"
|
||||||
@@ -121,7 +121,7 @@ echo "deploy create output file test: passed"
|
|||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
# Check logs command works
|
# Check logs command works
|
||||||
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
if [[ "$log_output_3" == *"Filesystem is fresh"* ]]; then
|
if [[ "$log_output_3" == *"filesystem is fresh"* ]]; then
|
||||||
echo "deployment logs test: passed"
|
echo "deployment logs test: passed"
|
||||||
else
|
else
|
||||||
echo "deployment logs test: FAILED"
|
echo "deployment logs test: FAILED"
|
||||||
@@ -158,7 +158,7 @@ $TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
|||||||
sleep 20
|
sleep 20
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
if [[ "$log_output_5" == *"Filesystem is old"* ]]; then
|
if [[ "$log_output_5" == *"filesystem is old"* ]]; then
|
||||||
echo "Retain volumes test: passed"
|
echo "Retain volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Retain volumes test: FAILED"
|
echo "Retain volumes test: FAILED"
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ if [ ! -f "$test_deployment_spec" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "deploy init test: passed"
|
echo "deploy init test: passed"
|
||||||
|
|
||||||
|
# Switch to a full path for bind mount.
|
||||||
|
sed -i "s|^\(\s*test-data-bind:$\)$|\1 ${test_deployment_dir}/data/test-data-bind|" $test_deployment_spec
|
||||||
|
|
||||||
$TEST_TARGET_SO --stack test deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
$TEST_TARGET_SO --stack test deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
||||||
# Check the deployment dir exists
|
# Check the deployment dir exists
|
||||||
if [ ! -d "$test_deployment_dir" ]; then
|
if [ ! -d "$test_deployment_dir" ]; then
|
||||||
@@ -99,7 +103,7 @@ if [ ! "$create_file_content" == "create-command-output-data" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Add a config file to be picked up by the ConfigMap before starting.
|
# Add a config file to be picked up by the ConfigMap before starting.
|
||||||
echo "dbfc7a4d-44a7-416d-b5f3-29842cc47650" > $test_deployment_dir/data/test-config/test_config
|
echo "dbfc7a4d-44a7-416d-b5f3-29842cc47650" > $test_deployment_dir/configmaps/test-config/test_config
|
||||||
|
|
||||||
echo "deploy create output file test: passed"
|
echo "deploy create output file test: passed"
|
||||||
# Try to start the deployment
|
# Try to start the deployment
|
||||||
@@ -107,11 +111,13 @@ $TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
|||||||
wait_for_pods_started
|
wait_for_pods_started
|
||||||
# Check logs command works
|
# Check logs command works
|
||||||
wait_for_log_output
|
wait_for_log_output
|
||||||
|
sleep 1
|
||||||
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
if [[ "$log_output_3" == *"Filesystem is fresh"* ]]; then
|
if [[ "$log_output_3" == *"filesystem is fresh"* ]]; then
|
||||||
echo "deployment logs test: passed"
|
echo "deployment logs test: passed"
|
||||||
else
|
else
|
||||||
echo "deployment logs test: FAILED"
|
echo "deployment logs test: FAILED"
|
||||||
|
echo $log_output_3
|
||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -140,6 +146,26 @@ else
|
|||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check that the bind-mount volume is mounted.
|
||||||
|
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_5" == *"/data: MOUNTED"* ]]; then
|
||||||
|
echo "deployment bind volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment bind volumes test: FAILED"
|
||||||
|
echo $log_output_5
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that the provisioner managed volume is mounted.
|
||||||
|
log_output_6=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_6" == *"/data2: MOUNTED"* ]]; then
|
||||||
|
echo "deployment provisioner volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment provisioner volumes test: FAILED"
|
||||||
|
echo $log_output_6
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Stop then start again and check the volume was preserved
|
# Stop then start again and check the volume was preserved
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
||||||
# Sleep a bit just in case
|
# Sleep a bit just in case
|
||||||
@@ -148,13 +174,26 @@ sleep 20
|
|||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
wait_for_pods_started
|
wait_for_pods_started
|
||||||
wait_for_log_output
|
wait_for_log_output
|
||||||
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
sleep 1
|
||||||
if [[ "$log_output_5" == *"Filesystem is old"* ]]; then
|
|
||||||
echo "Retain volumes test: passed"
|
log_output_10=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_10" == *"/data filesystem is old"* ]]; then
|
||||||
|
echo "Retain bind volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Retain volumes test: FAILED"
|
echo "Retain bind volumes test: FAILED"
|
||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# These volumes will be completely destroyed by the kind delete/create, because they lived inside
|
||||||
|
# the kind container. So, unlike the bind-mount case, they will appear fresh after the restart.
|
||||||
|
log_output_11=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_11" == *"/data2 filesystem is fresh"* ]]; then
|
||||||
|
echo "Fresh provisioner volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "Fresh provisioner volumes test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Stop and clean up
|
# Stop and clean up
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
echo "Test passed"
|
echo "Test passed"
|
||||||
|
|||||||
Reference in New Issue
Block a user