forked from cerc-io/stack-orchestrator
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
faa48e8ca8 | ||
|
|
bfbcfb7904 | ||
|
|
037ffe9a47 | ||
|
|
3d5ececba5 | ||
|
|
01ab438c7e | ||
|
|
6848fc33cf | ||
|
|
36bb068983 | ||
|
|
c85f31760d | ||
|
|
25a2b70f2c | ||
|
|
2fcd416e29 | ||
|
|
6629017d6a | ||
|
|
1c30441000 | ||
|
|
b398050787 | ||
|
|
12ec1bec43 | ||
|
|
62af03077f | ||
|
|
428b05158e | ||
|
|
a750b645b9 | ||
|
|
f67668c1be | ||
|
|
2c17b901a8 | ||
|
|
886f1f322c | ||
|
|
d279aedf84 | ||
|
|
b7f215d9bf | ||
|
|
eca52b10b7 | ||
|
|
b9128841e4 | ||
|
|
0a302ea555 | ||
|
|
aa0f60baa1 | ||
|
|
cef73d8de2 | ||
|
|
7d0f2adb46 | ||
|
|
5fdee25dc1 | ||
|
|
554f05de87 | ||
|
|
b4fbee9b13 |
@@ -6,6 +6,8 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- '!**'
|
- '!**'
|
||||||
- '.gitea/workflows/triggers/fixturenet-eth-plugeth-test'
|
- '.gitea/workflows/triggers/fixturenet-eth-plugeth-test'
|
||||||
|
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||||
|
- cron: '2 14 * * *'
|
||||||
|
|
||||||
# Needed until we can incorporate docker startup into the executor container
|
# Needed until we can incorporate docker startup into the executor container
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -6,11 +6,8 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- '!**'
|
- '!**'
|
||||||
- '.gitea/workflows/triggers/fixturenet-laconicd-test'
|
- '.gitea/workflows/triggers/fixturenet-laconicd-test'
|
||||||
|
schedule:
|
||||||
# Needed until we can incorporate docker startup into the executor container
|
- cron: '1 13 * * *'
|
||||||
env:
|
|
||||||
DOCKER_HOST: unix:///var/run/dind.sock
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -47,9 +44,5 @@ jobs:
|
|||||||
run: ./scripts/create_build_tag_file.sh
|
run: ./scripts/create_build_tag_file.sh
|
||||||
- name: "Build local shiv package"
|
- name: "Build local shiv package"
|
||||||
run: ./scripts/build_shiv_package.sh
|
run: ./scripts/build_shiv_package.sh
|
||||||
- name: Start dockerd # Also needed until we can incorporate into the executor
|
|
||||||
run: |
|
|
||||||
dockerd -H $DOCKER_HOST --userland-proxy=false &
|
|
||||||
sleep 5
|
|
||||||
- name: "Run fixturenet-laconicd tests"
|
- name: "Run fixturenet-laconicd tests"
|
||||||
run: ./tests/fixturenet-laconicd/run-test.sh
|
run: ./tests/fixturenet-laconicd/run-test.sh
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ on:
|
|||||||
branches: '*'
|
branches: '*'
|
||||||
paths:
|
paths:
|
||||||
- '!**'
|
- '!**'
|
||||||
- '.gitea/workflows/triggers/fixturenet-laconicd-test'
|
- '.gitea/workflows/triggers/test-k8s-deploy'
|
||||||
|
- '.gitea/workflows/test-k8s-deploy.yml'
|
||||||
|
- 'tests/k8s-deploy/run-deploy-test.sh'
|
||||||
|
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||||
|
- cron: '3 15 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: "Run deploy test suite on kind/k8s"
|
name: "Run deploy test suite on kind/k8s"
|
||||||
runs-on: ubuntu-22.04-with-syn-ethdb
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: "Clone project repository"
|
- name: "Clone project repository"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -34,9 +38,15 @@ jobs:
|
|||||||
run: ./scripts/create_build_tag_file.sh
|
run: ./scripts/create_build_tag_file.sh
|
||||||
- name: "Build local shiv package"
|
- name: "Build local shiv package"
|
||||||
run: ./scripts/build_shiv_package.sh
|
run: ./scripts/build_shiv_package.sh
|
||||||
|
- name: "Check cgroups version"
|
||||||
|
run: mount | grep cgroup
|
||||||
- name: "Install kind"
|
- name: "Install kind"
|
||||||
run: ./tests/scripts/install-kind.sh
|
run: ./tests/scripts/install-kind.sh
|
||||||
- name: "Install Kubectl"
|
- name: "Install Kubectl"
|
||||||
run: ./tests/scripts/install-kubectl.sh
|
run: ./tests/scripts/install-kubectl.sh
|
||||||
- name: "Run k8s deployment test"
|
- name: "Run k8s deployment test"
|
||||||
run: ./tests/k8s-deploy/run-deploy-test.sh
|
run: |
|
||||||
|
source /opt/bash-utils/cgroup-helper.sh
|
||||||
|
join_cgroup
|
||||||
|
./tests/k8s-deploy/run-deploy-test.sh
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
Change this file to trigger running the fixturenet-eth-plugeth-test CI job
|
Change this file to trigger running the fixturenet-eth-plugeth-test CI job
|
||||||
trigger
|
trigger
|
||||||
|
trigger
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
Change this file to trigger running the fixturenet-laconicd-test CI job
|
Change this file to trigger running the fixturenet-laconicd-test CI job
|
||||||
|
Trigger
|
||||||
|
Trigger
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
Change this file to trigger running the test-k8s-deploy CI job
|
Change this file to trigger running the test-k8s-deploy CI job
|
||||||
|
Trigger test on PR branch
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ from stack_orchestrator.base import get_npm_registry_url
|
|||||||
# TODO: find a place for this
|
# TODO: find a place for this
|
||||||
# epilog="Config provided either in .env or settings.ini or env vars: CERC_REPO_BASE_DIR (defaults to ~/cerc)"
|
# epilog="Config provided either in .env or settings.ini or env vars: CERC_REPO_BASE_DIR (defaults to ~/cerc)"
|
||||||
|
|
||||||
|
|
||||||
def make_container_build_env(dev_root_path: str,
|
def make_container_build_env(dev_root_path: str,
|
||||||
container_build_dir: str,
|
container_build_dir: str,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
@@ -104,6 +105,9 @@ def process_container(stack: str,
|
|||||||
build_command = os.path.join(container_build_dir,
|
build_command = os.path.join(container_build_dir,
|
||||||
"default-build.sh") + f" {default_container_tag} {repo_dir_or_build_dir}"
|
"default-build.sh") + f" {default_container_tag} {repo_dir_or_build_dir}"
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
|
# No PATH at all causes failures with podman.
|
||||||
|
if "PATH" not in container_build_env:
|
||||||
|
container_build_env["PATH"] = os.environ["PATH"]
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"Executing: {build_command} with environment: {container_build_env}")
|
print(f"Executing: {build_command} with environment: {container_build_env}")
|
||||||
build_result = subprocess.run(build_command, shell=True, env=container_build_env)
|
build_result = subprocess.run(build_command, shell=True, env=container_build_env)
|
||||||
@@ -119,6 +123,7 @@ def process_container(stack: str,
|
|||||||
else:
|
else:
|
||||||
print("Skipped")
|
print("Skipped")
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option('--include', help="only build these containers")
|
@click.option('--include', help="only build these containers")
|
||||||
@click.option('--exclude', help="don\'t build these containers")
|
@click.option('--exclude', help="don\'t build these containers")
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ from decouple import config
|
|||||||
import click
|
import click
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from stack_orchestrator.build import build_containers
|
from stack_orchestrator.build import build_containers
|
||||||
|
from stack_orchestrator.deploy.webapp.util import determine_base_container
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option('--base-container', default="cerc/nextjs-base")
|
@click.option('--base-container')
|
||||||
@click.option('--source-repo', help="directory containing the webapp to build", required=True)
|
@click.option('--source-repo', help="directory containing the webapp to build", required=True)
|
||||||
@click.option("--force-rebuild", is_flag=True, default=False, help="Override dependency checking -- always rebuild")
|
@click.option("--force-rebuild", is_flag=True, default=False, help="Override dependency checking -- always rebuild")
|
||||||
@click.option("--extra-build-args", help="Supply extra arguments to build")
|
@click.option("--extra-build-args", help="Supply extra arguments to build")
|
||||||
@@ -57,6 +58,9 @@ def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, t
|
|||||||
if not quiet:
|
if not quiet:
|
||||||
print(f'Dev Root is: {dev_root_path}')
|
print(f'Dev Root is: {dev_root_path}')
|
||||||
|
|
||||||
|
if not base_container:
|
||||||
|
base_container = determine_base_container(source_repo)
|
||||||
|
|
||||||
# First build the base container.
|
# First build the base container.
|
||||||
container_build_env = build_containers.make_container_build_env(dev_root_path, container_build_dir, debug,
|
container_build_env = build_containers.make_container_build_env(dev_root_path, container_build_dir, debug,
|
||||||
force_rebuild, extra_build_args)
|
force_rebuild, extra_build_args)
|
||||||
@@ -64,13 +68,12 @@ def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, t
|
|||||||
build_containers.process_container(None, base_container, container_build_dir, container_build_env, dev_root_path, quiet,
|
build_containers.process_container(None, base_container, container_build_dir, container_build_env, dev_root_path, quiet,
|
||||||
verbose, dry_run, continue_on_error)
|
verbose, dry_run, continue_on_error)
|
||||||
|
|
||||||
|
|
||||||
# Now build the target webapp. We use the same build script, but with a different Dockerfile and work dir.
|
# Now build the target webapp. We use the same build script, but with a different Dockerfile and work dir.
|
||||||
container_build_env["CERC_WEBAPP_BUILD_RUNNING"] = "true"
|
container_build_env["CERC_WEBAPP_BUILD_RUNNING"] = "true"
|
||||||
container_build_env["CERC_CONTAINER_BUILD_WORK_DIR"] = os.path.abspath(source_repo)
|
container_build_env["CERC_CONTAINER_BUILD_WORK_DIR"] = os.path.abspath(source_repo)
|
||||||
container_build_env["CERC_CONTAINER_BUILD_DOCKERFILE"] = os.path.join(container_build_dir,
|
container_build_env["CERC_CONTAINER_BUILD_DOCKERFILE"] = os.path.join(container_build_dir,
|
||||||
base_container.replace("/", "-"),
|
base_container.replace("/", "-"),
|
||||||
"Dockerfile.webapp")
|
"Dockerfile.webapp")
|
||||||
if not tag:
|
if not tag:
|
||||||
webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1]
|
webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1]
|
||||||
container_build_env["CERC_CONTAINER_BUILD_TAG"] = f"cerc/{webapp_name}:local"
|
container_build_env["CERC_CONTAINER_BUILD_TAG"] = f"cerc/{webapp_name}:local"
|
||||||
|
|||||||
@@ -2,29 +2,29 @@ services:
|
|||||||
laconicd:
|
laconicd:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: cerc/laconicd:local
|
image: cerc/laconicd:local
|
||||||
entrypoint: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"]
|
command: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"]
|
||||||
volumes:
|
volumes:
|
||||||
# The cosmos-sdk node's database directory:
|
# The cosmos-sdk node's database directory:
|
||||||
- laconicd-data:/root/.laconicd
|
- laconicd-data:/root/.laconicd
|
||||||
# TODO: look at folding these scripts into the container
|
# TODO: look at folding these scripts into the container
|
||||||
- ../config/fixturenet-laconicd/create-fixturenet.sh:/docker-entrypoint-scripts.d/create-fixturenet.sh
|
- laconicd-scripts:/docker-entrypoint-scripts.d:ro
|
||||||
- ../config/fixturenet-laconicd/export-mykey.sh:/docker-entrypoint-scripts.d/export-mykey.sh
|
|
||||||
- ../config/fixturenet-laconicd/export-myaddress.sh:/docker-entrypoint-scripts.d/export-myaddress.sh
|
|
||||||
# TODO: determine which of the ports below is really needed
|
# TODO: determine which of the ports below is really needed
|
||||||
ports:
|
ports:
|
||||||
- "6060"
|
- "6060"
|
||||||
- "26657"
|
- "26657"
|
||||||
- "26656"
|
- "26656"
|
||||||
- "9473:9473"
|
- "9473"
|
||||||
- "8545"
|
- "8545"
|
||||||
- "8546"
|
- "8546"
|
||||||
- "9090"
|
- "9090"
|
||||||
- "9091"
|
- "9091"
|
||||||
- "1317:1317"
|
- "1317"
|
||||||
cli:
|
cli:
|
||||||
image: cerc/laconic-registry-cli:local
|
image: cerc/laconic-registry-cli:local
|
||||||
volumes:
|
volumes:
|
||||||
- ../config/fixturenet-laconicd/registry-cli-config-template.yml:/registry-cli-config-template.yml
|
- cli-config:/registry-cli-config
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
laconicd-data:
|
laconicd-data:
|
||||||
|
laconicd-scripts:
|
||||||
|
cli-config:
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
version: "3.2"
|
|
||||||
|
|
||||||
services:
|
|
||||||
geojson:
|
|
||||||
image: cerc/geojson:local
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 0.0.0.0:8080:8080
|
|
||||||
@@ -9,9 +9,7 @@ services:
|
|||||||
- laconicd-config:/root/.laconicd/config
|
- laconicd-config:/root/.laconicd/config
|
||||||
- laconicd-keyring:/root/.laconicd/keyring-test
|
- laconicd-keyring:/root/.laconicd/keyring-test
|
||||||
# TODO: look at folding these scripts into the container
|
# TODO: look at folding these scripts into the container
|
||||||
- ../config/mainnet-laconicd/scripts/run-laconicd.sh:/opt/run-laconicd.sh
|
- laconicd-scripts:/docker-entrypoint-scripts.d:ro
|
||||||
- ../config/mainnet-laconicd/scripts/export-mykey.sh:/docker-entrypoint-scripts.d/export-mykey.sh
|
|
||||||
- ../config/mainnet-laconicd/scripts/export-myaddress.sh:/docker-entrypoint-scripts.d/export-myaddress.sh
|
|
||||||
# TODO: determine which of the ports below is really needed
|
# TODO: determine which of the ports below is really needed
|
||||||
ports:
|
ports:
|
||||||
- "6060"
|
- "6060"
|
||||||
@@ -26,9 +24,11 @@ services:
|
|||||||
cli:
|
cli:
|
||||||
image: cerc/laconic-registry-cli:local
|
image: cerc/laconic-registry-cli:local
|
||||||
volumes:
|
volumes:
|
||||||
- ../config/mainnet-laconicd/registry-cli-config-template.yml:/registry-cli-config-template.yml
|
- cli-config:/registry-cli-config
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
laconicd-data:
|
laconicd-data:
|
||||||
laconicd-config:
|
laconicd-config:
|
||||||
|
laconicd-scripts:
|
||||||
laconicd-keyring:
|
laconicd-keyring:
|
||||||
|
cli-config:
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ services:
|
|||||||
CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED}
|
CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED}
|
||||||
volumes:
|
volumes:
|
||||||
- test-data:/data
|
- test-data:/data
|
||||||
|
- test-config:/config:ro
|
||||||
ports:
|
ports:
|
||||||
- "80"
|
- "80"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
test-data:
|
test-data:
|
||||||
|
test-config:
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# TODO: this file is now an unmodified copy of cerc-io/laconicd/init.sh
|
||||||
|
# so we should have a mechanism to bundle it inside the container rather than link from here
|
||||||
|
# at deploy time.
|
||||||
|
|
||||||
|
KEY="mykey"
|
||||||
|
CHAINID="laconic_9000-1"
|
||||||
|
MONIKER="localtestnet"
|
||||||
|
KEYRING="test"
|
||||||
|
KEYALGO="eth_secp256k1"
|
||||||
|
LOGLEVEL="info"
|
||||||
|
# trace evm
|
||||||
|
TRACE="--trace"
|
||||||
|
# TRACE=""
|
||||||
|
|
||||||
|
if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
|
||||||
|
# validate dependencies are installed
|
||||||
|
command -v jq > /dev/null 2>&1 || { echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"; exit 1; }
|
||||||
|
|
||||||
|
# remove existing daemon and client
|
||||||
|
rm -rf $HOME/.laconicd/*
|
||||||
|
rm -rf $HOME/.laconic/*
|
||||||
|
|
||||||
|
if [ -n "`which make`" ]; then
|
||||||
|
make install
|
||||||
|
fi
|
||||||
|
|
||||||
|
laconicd config keyring-backend $KEYRING
|
||||||
|
laconicd config chain-id $CHAINID
|
||||||
|
|
||||||
|
# if $KEY exists it should be deleted
|
||||||
|
laconicd keys add $KEY --keyring-backend $KEYRING --algo $KEYALGO
|
||||||
|
|
||||||
|
# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer)
|
||||||
|
laconicd init $MONIKER --chain-id $CHAINID
|
||||||
|
|
||||||
|
# Change parameter token denominations to aphoton
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["staking"]["params"]["bond_denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["crisis"]["constant_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
# Custom modules
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["record_rent"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_commit_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_reveal_fee"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_minimum_bid"]["denom"]="aphoton"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
|
||||||
|
if [[ "$TEST_REGISTRY_EXPIRY" == "true" ]]; then
|
||||||
|
echo "Setting timers for expiry tests."
|
||||||
|
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["record_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_grace_period"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$TEST_AUCTION_ENABLED" == "true" ]]; then
|
||||||
|
echo "Enabling auction and setting timers."
|
||||||
|
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_enabled"]=true' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_rent_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_grace_period"]="300s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_commits_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.app_state["registry"]["params"]["authority_auction_reveals_duration"]="60s"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
# increase block time (?)
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.consensus_params["block"]["time_iota_ms"]="1000"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
|
||||||
|
# Set gas limit in genesis
|
||||||
|
cat $HOME/.laconicd/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="10000000"' > $HOME/.laconicd/config/tmp_genesis.json && mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
|
||||||
|
|
||||||
|
# disable produce empty block
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
sed -i '' 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml
|
||||||
|
else
|
||||||
|
sed -i 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $1 == "pending" ]]; then
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
sed -i '' 's/create_empty_blocks_interval = "0s"/create_empty_blocks_interval = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_propose = "3s"/timeout_propose = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_commit = "5s"/timeout_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i '' 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
else
|
||||||
|
sed -i 's/create_empty_blocks_interval = "0s"/create_empty_blocks_interval = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_propose = "3s"/timeout_propose = "30s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_commit = "5s"/timeout_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
sed -i 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' $HOME/.laconicd/config/config.toml
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Allocate genesis accounts (cosmos formatted addresses)
|
||||||
|
laconicd add-genesis-account $KEY 100000000000000000000000000aphoton --keyring-backend $KEYRING
|
||||||
|
|
||||||
|
# Sign genesis transaction
|
||||||
|
laconicd gentx $KEY 1000000000000000000000aphoton --keyring-backend $KEYRING --chain-id $CHAINID
|
||||||
|
|
||||||
|
# Collect genesis tx
|
||||||
|
laconicd collect-gentxs
|
||||||
|
|
||||||
|
# Run this to ensure everything worked and that the genesis file is setup correctly
|
||||||
|
laconicd validate-genesis
|
||||||
|
|
||||||
|
if [[ $1 == "pending" ]]; then
|
||||||
|
echo "pending mode is on, please wait for the first block committed."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Using existing database at $HOME/.laconicd. To replace, run '`basename $0` clean'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start the node (remove the --pruning=nothing flag if historical queries are not needed)
|
||||||
|
laconicd start --pruning=nothing --evm.tracer=json $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --json-rpc.api eth,txpool,personal,net,debug,web3,miner --api.enable --gql-server --gql-playground
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
laconicd keys show mykey | grep address | cut -d ' ' -f 3
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo y | laconicd keys export mykey --unarmored-hex --unsafe
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
cns:
|
||||||
|
restEndpoint: 'http://laconicd:1317'
|
||||||
|
gqlEndpoint: 'http://laconicd:9473/api'
|
||||||
|
userKey: REPLACE_WITH_MYKEY
|
||||||
|
bondId:
|
||||||
|
chainId: laconic_9000-1
|
||||||
|
gas: 350000
|
||||||
|
fees: 200000aphoton
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Build the birbit image
|
|
||||||
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
|
||||||
docker build -t cerc/birbit:local -f ${CERC_REPO_BASE_DIR}/birbit/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/birbit
|
|
||||||
@@ -10,9 +10,10 @@ COPY genesis /opt/genesis
|
|||||||
COPY --from=ethgen /usr/local/bin/eth2-testnet-genesis /usr/local/bin/
|
COPY --from=ethgen /usr/local/bin/eth2-testnet-genesis /usr/local/bin/
|
||||||
COPY --from=ethgen /usr/local/bin/eth2-val-tools /usr/local/bin/
|
COPY --from=ethgen /usr/local/bin/eth2-val-tools /usr/local/bin/
|
||||||
COPY --from=ethgen /apps /apps
|
COPY --from=ethgen /apps /apps
|
||||||
RUN cd /apps/el-gen && pip3 install -r requirements.txt
|
RUN cd /apps/el-gen && pip3 install --break-system-packages -r requirements.txt
|
||||||
# web3==5.24.0 used by el-gen is broken on python 3.11
|
# web3==5.24.0 used by el-gen is broken on python 3.11
|
||||||
RUN pip3 install --upgrade "web3==6.5.0"
|
RUN pip3 install --break-system-packages --upgrade "web3==6.5.0"
|
||||||
|
RUN pip3 install --break-system-packages --upgrade "typing-extensions"
|
||||||
|
|
||||||
# Build genesis config
|
# Build genesis config
|
||||||
RUN apk add --no-cache make bash envsubst jq
|
RUN apk add --no-cache make bash envsubst jq
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
FROM node:14
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN npm install && npm run build
|
|
||||||
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
CMD ["npm", "run", "serve"]
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Build the geojson image
|
|
||||||
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
|
||||||
docker build -t cerc/geojson:local -f ${CERC_CONTAINER_BASE_DIR}/cerc-geojson/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/geojson.io
|
|
||||||
#docker build -t cerc/geojson:local -f ${CERC_REPO_BASE_DIR}/geojson.io/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/geojson.io
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
sed 's/REPLACE_WITH_MYKEY/'${1}'/' registry-cli-config-template.yml > config.yml
|
sed 's/REPLACE_WITH_MYKEY/'${1}'/' /registry-cli-config/registry-cli-config-template.yml > config.yml
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ RUN \
|
|||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
&& apt-get -y install --no-install-recommends jq gettext-base
|
&& apt-get -y install --no-install-recommends jq gettext-base procps
|
||||||
|
|
||||||
# [Optional] Uncomment if you want to install more global node modules
|
# [Optional] Uncomment if you want to install more global node modules
|
||||||
# RUN su node -c "npm install -g <your-package-list-here>"
|
# RUN su node -c "npm install -g <your-package-list-here>"
|
||||||
|
|
||||||
# Expose port for http
|
# Expose port for http
|
||||||
EXPOSE 3000
|
EXPOSE 80
|
||||||
|
|
||||||
COPY /scripts /scripts
|
COPY /scripts /scripts
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -58,4 +58,4 @@ if [ "$CERC_NEXTJS_SKIP_GENERATE" != "true" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$CERC_BUILD_TOOL start . -p ${CERC_LISTEN_PORT:-3000}
|
$CERC_BUILD_TOOL start . -- -p ${CERC_LISTEN_PORT:-80}
|
||||||
|
|||||||
@@ -17,5 +17,20 @@ fi
|
|||||||
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
|
||||||
|
|
||||||
|
if [ -d "/config" ]; then
|
||||||
|
echo "/config: EXISTS"
|
||||||
|
for f in /config/*; do
|
||||||
|
if [[ -f "$f" ]] || [[ -L "$f" ]]; then
|
||||||
|
echo "$f:"
|
||||||
|
cat "$f"
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "/config: does NOT EXIST"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run nginx which will block here forever
|
# Run nginx which will block here forever
|
||||||
/usr/sbin/nginx -g "daemon off;"
|
/usr/sbin/nginx -g "daemon off;"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile
|
# Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile
|
||||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
ARG VARIANT=18-bullseye
|
ARG VARIANT=20-bullseye
|
||||||
FROM node:${VARIANT}
|
FROM node:${VARIANT}
|
||||||
|
|
||||||
ARG USERNAME=node
|
ARG USERNAME=node
|
||||||
@@ -28,7 +28,7 @@ RUN \
|
|||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
&& apt-get -y install --no-install-recommends jq
|
&& apt-get -y install --no-install-recommends jq gettext-base
|
||||||
|
|
||||||
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
||||||
# ARG EXTRA_NODE_VERSION=10
|
# ARG EXTRA_NODE_VERSION=10
|
||||||
@@ -37,9 +37,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|||||||
# We do this to get a yq binary from the published container, for the correct architecture we're building here
|
# We do this to get a yq binary from the published container, for the correct architecture we're building here
|
||||||
COPY --from=docker.io/mikefarah/yq:latest /usr/bin/yq /usr/local/bin/yq
|
COPY --from=docker.io/mikefarah/yq:latest /usr/bin/yq /usr/local/bin/yq
|
||||||
|
|
||||||
RUN mkdir -p /scripts
|
COPY scripts /scripts
|
||||||
COPY ./apply-webapp-config.sh /scripts
|
|
||||||
COPY ./start-serving-app.sh /scripts
|
|
||||||
|
|
||||||
# [Optional] Uncomment if you want to install more global node modules
|
# [Optional] Uncomment if you want to install more global node modules
|
||||||
# RUN su node -c "npm install -g <your-package-list-here>"
|
# RUN su node -c "npm install -g <your-package-list-here>"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
FROM cerc/webapp-base:local as builder
|
||||||
|
|
||||||
|
ARG CERC_BUILD_TOOL
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN rm -rf node_modules build .next*
|
||||||
|
RUN /scripts/build-app.sh /app build /data
|
||||||
|
|
||||||
|
FROM cerc/webapp-base:local
|
||||||
|
COPY --from=builder /data /data
|
||||||
@@ -1,9 +1,29 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Build cerc/laconic-registry-cli
|
# Build cerc/webapp-base
|
||||||
|
|
||||||
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
|
||||||
# See: https://stackoverflow.com/a/246128/1701505
|
# See: https://stackoverflow.com/a/246128/1701505
|
||||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
docker build -t cerc/webapp-base:local ${build_command_args} -f ${SCRIPT_DIR}/Dockerfile ${SCRIPT_DIR}
|
CERC_CONTAINER_BUILD_WORK_DIR=${CERC_CONTAINER_BUILD_WORK_DIR:-$SCRIPT_DIR}
|
||||||
|
CERC_CONTAINER_BUILD_DOCKERFILE=${CERC_CONTAINER_BUILD_DOCKERFILE:-$SCRIPT_DIR/Dockerfile}
|
||||||
|
CERC_CONTAINER_BUILD_TAG=${CERC_CONTAINER_BUILD_TAG:-cerc/webapp-base:local}
|
||||||
|
|
||||||
|
docker build -t $CERC_CONTAINER_BUILD_TAG ${build_command_args} -f $CERC_CONTAINER_BUILD_DOCKERFILE $CERC_CONTAINER_BUILD_WORK_DIR
|
||||||
|
|
||||||
|
if [ $? -eq 0 ] && [ "$CERC_CONTAINER_BUILD_TAG" != "cerc/webapp-base:local" ]; then
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
#################################################################
|
||||||
|
|
||||||
|
Built host container for $CERC_CONTAINER_BUILD_WORK_DIR with tag:
|
||||||
|
|
||||||
|
$CERC_CONTAINER_BUILD_TAG
|
||||||
|
|
||||||
|
To test locally run:
|
||||||
|
|
||||||
|
laconic-so run-webapp --image $CERC_CONTAINER_BUILD_TAG --env-file /path/to/environment.env
|
||||||
|
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK_DIR="${1:-./}"
|
||||||
|
|
||||||
|
cd "${WORK_DIR}" || exit 1
|
||||||
|
|
||||||
|
if [ -f ".env" ]; then
|
||||||
|
TMP_ENV=`mktemp`
|
||||||
|
declare -px > $TMP_ENV
|
||||||
|
set -a
|
||||||
|
source .env
|
||||||
|
source $TMP_ENV
|
||||||
|
set +a
|
||||||
|
rm -f $TMP_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in $(find . -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do
|
||||||
|
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},();]' '\n' | egrep -o -e '^"CERC_RUNTIME_ENV_[^\"]+"' -e '^"LACONIC_HOSTED_CONFIG_[^\"]+"'); do
|
||||||
|
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
||||||
|
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
||||||
|
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
||||||
|
if [ "$CERC_RETAIN_ENV_QUOTES" != "true" ]; then
|
||||||
|
cur_val=$(sed "s/^[\"']//" <<< "$cur_val" | sed "s/[\"']//")
|
||||||
|
fi
|
||||||
|
esc_val=$(sed 's/[&/\]/\\&/g' <<< "$cur_val")
|
||||||
|
echo "$f: $cur_name=$cur_val"
|
||||||
|
sed -i "s/$orig_name/$esc_val/g" $f
|
||||||
|
done
|
||||||
|
done
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
CERC_BUILD_TOOL="${CERC_BUILD_TOOL}"
|
||||||
|
WORK_DIR="${1:-/app}"
|
||||||
|
OUTPUT_DIR="${2:-build}"
|
||||||
|
DEST_DIR="${3:-/data}"
|
||||||
|
|
||||||
|
if [ -f "${WORK_DIR}/package.json" ]; then
|
||||||
|
echo "Building node-based webapp ..."
|
||||||
|
cd "${WORK_DIR}" || exit 1
|
||||||
|
|
||||||
|
if [ -z "$CERC_BUILD_TOOL" ]; then
|
||||||
|
if [ -f "yarn.lock" ]; then
|
||||||
|
CERC_BUILD_TOOL=yarn
|
||||||
|
else
|
||||||
|
CERC_BUILD_TOOL=npm
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
$CERC_BUILD_TOOL install || exit 1
|
||||||
|
$CERC_BUILD_TOOL build || exit 1
|
||||||
|
|
||||||
|
rm -rf "${DEST_DIR}"
|
||||||
|
mv "${WORK_DIR}/${OUTPUT_DIR}" "${DEST_DIR}"
|
||||||
|
else
|
||||||
|
echo "Copying static app ..."
|
||||||
|
mv "${WORK_DIR}" "${DEST_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
CERC_WEBAPP_FILES_DIR="${CERC_WEBAPP_FILES_DIR:-/data}"
|
||||||
|
CERC_ENABLE_CORS="${CERC_ENABLE_CORS:-false}"
|
||||||
|
|
||||||
|
if [ "true" == "$CERC_ENABLE_CORS" ]; then
|
||||||
|
CERC_HTTP_EXTRA_ARGS="$CERC_HTTP_EXTRA_ARGS --cors"
|
||||||
|
fi
|
||||||
|
|
||||||
|
/scripts/apply-webapp-config.sh /config/config.yml ${CERC_WEBAPP_FILES_DIR}
|
||||||
|
/scripts/apply-runtime-env.sh ${CERC_WEBAPP_FILES_DIR}
|
||||||
|
http-server $CERC_HTTP_EXTRA_ARGS -p ${CERC_LISTEN_PORT:-80} ${CERC_WEBAPP_FILES_DIR}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
|
|
||||||
CERC_WEBAPP_FILES_DIR="${CERC_WEBAPP_FILES_DIR:-/data}"
|
|
||||||
|
|
||||||
/scripts/apply-webapp-config.sh /config/config.yml ${CERC_WEBAPP_FILES_DIR}
|
|
||||||
http-server -p 80 ${CERC_WEBAPP_FILES_DIR}
|
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build cerc/webapp-deployer-backend
|
||||||
|
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
|
||||||
|
# See: https://stackoverflow.com/a/246128/1701505
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
docker build -t cerc/webapp-deployer-backend:local ${build_command_args} ${CERC_REPO_BASE_DIR}/webapp-deployment-status-api
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# birbit
|
|
||||||
|
|
||||||
takes geo-tagged images, publishes the metadata on chain, then generates a geojson front end of that geo data
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
version: "0.1"
|
|
||||||
name: birbit
|
|
||||||
repos:
|
|
||||||
#- github.com/zramsay/birbit
|
|
||||||
- github.com/mapbox/geojson.io
|
|
||||||
containers:
|
|
||||||
#- cerc/birbit
|
|
||||||
- cerc/geojson
|
|
||||||
pods:
|
|
||||||
# configurable process that reads from chain and generates a view for the front end
|
|
||||||
# for MVP is basically does nothing, b/c FE view is reading all records
|
|
||||||
# imagine a 'watcher' for a specific view
|
|
||||||
#- birbit
|
|
||||||
# front end, gets its file from kubo;
|
|
||||||
# https://github.com/mapbox/geojson.io/blob/main/API.md#datadatatextx-url
|
|
||||||
# deployed via kubernetes
|
|
||||||
- geojson
|
|
||||||
# hosts all the dot geojson files that are required to generated views
|
|
||||||
- kubo
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Copyright © 2022-2024 Vulcanize
|
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
from stack_orchestrator.util import get_yaml, get_config_file_dir
|
||||||
|
from stack_orchestrator.deploy.deploy_types import DeployCommandContext
|
||||||
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import copy
|
||||||
|
|
||||||
|
default_spec_file_content = ""
|
||||||
|
|
||||||
|
|
||||||
|
def _stack_config_dir(context: DeploymentContext) -> Path:
|
||||||
|
return get_config_file_dir().joinpath(context.stack.name)
|
||||||
|
|
||||||
|
|
||||||
|
def _deployment_volume_dir(context: DeploymentContext, volume_name: str) -> Path:
|
||||||
|
return context.deployment_dir.joinpath("data", volume_name)
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_laconicd_scripts(context: DeploymentContext):
|
||||||
|
scripts = [
|
||||||
|
"create-fixturenet.sh",
|
||||||
|
"export-mykey.sh",
|
||||||
|
"export-myaddress.sh"
|
||||||
|
]
|
||||||
|
destination_dir = _deployment_volume_dir(context, "laconicd-scripts")
|
||||||
|
for script in scripts:
|
||||||
|
# ../config/fixturenet-laconicd/
|
||||||
|
source_file = _stack_config_dir(context).joinpath(script)
|
||||||
|
copy(source_file, destination_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_cli_template(context: DeploymentContext):
|
||||||
|
destination_dir = _deployment_volume_dir(context, "cli-config")
|
||||||
|
# ../config/fixturenet-laconicd/
|
||||||
|
source_file = _stack_config_dir(context).joinpath("registry-cli-config-template.yml")
|
||||||
|
copy(source_file, destination_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(command_context: DeployCommandContext, parameters, extra_args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def init(command_context: DeployCommandContext):
|
||||||
|
yaml = get_yaml()
|
||||||
|
return yaml.load(default_spec_file_content)
|
||||||
|
|
||||||
|
|
||||||
|
def create(context: DeploymentContext, extra_args):
|
||||||
|
_copy_laconicd_scripts(context)
|
||||||
|
_copy_cli_template(context)
|
||||||
|
|
||||||
|
|
||||||
|
def get_state(command_context: DeployCommandContext):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def change_state(command_context: DeployCommandContext):
|
||||||
|
pass
|
||||||
@@ -18,7 +18,7 @@ $ laconic-so --stack mainnet-eth build-containers
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ laconic-so --stack mainnet-eth deploy init --map-ports-to-host any-same --output mainnet-eth-spec.yml
|
$ laconic-so --stack mainnet-eth deploy init --map-ports-to-host any-same --output mainnet-eth-spec.yml
|
||||||
$ laconic-so deploy create --spec-file mainnet-eth-spec.yml --deployment-dir mainnet-eth-deployment
|
$ laconic-so deploy --stack mainnet-eth create --spec-file mainnet-eth-spec.yml --deployment-dir mainnet-eth-deployment
|
||||||
```
|
```
|
||||||
## Start the stack
|
## Start the stack
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
version: "1.0"
|
||||||
|
name: webapp-deployer-backend
|
||||||
|
description: "Deployer for webapps"
|
||||||
|
repos:
|
||||||
|
- git.vdb.to:telackey/webapp-deployment-status-api
|
||||||
|
containers:
|
||||||
|
- cerc/webapp-deployer-backend
|
||||||
|
pods:
|
||||||
|
- name: webapp-deployer-backend
|
||||||
|
repository: git.vdb.to:telackey/webapp-deployment-status-api
|
||||||
|
path: ./
|
||||||
@@ -17,6 +17,7 @@ from pathlib import Path
|
|||||||
from python_on_whales import DockerClient, DockerException
|
from python_on_whales import DockerClient, DockerException
|
||||||
from stack_orchestrator.deploy.deployer import Deployer, DeployerException, DeployerConfigGenerator
|
from stack_orchestrator.deploy.deployer import Deployer, DeployerException, DeployerConfigGenerator
|
||||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||||
|
from stack_orchestrator.opts import opts
|
||||||
|
|
||||||
|
|
||||||
class DockerDeployer(Deployer):
|
class DockerDeployer(Deployer):
|
||||||
@@ -29,60 +30,69 @@ class DockerDeployer(Deployer):
|
|||||||
self.type = type
|
self.type = type
|
||||||
|
|
||||||
def up(self, detach, services):
|
def up(self, detach, services):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.up(detach=detach, services=services)
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.up(detach=detach, services=services)
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def down(self, timeout, volumes):
|
def down(self, timeout, volumes):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.down(timeout=timeout, volumes=volumes)
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.down(timeout=timeout, volumes=volumes)
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.restart()
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.restart()
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
for p in self.docker.compose.ps():
|
try:
|
||||||
print(f"{p.name}\t{p.state.status}")
|
for p in self.docker.compose.ps():
|
||||||
except DockerException as e:
|
print(f"{p.name}\t{p.state.status}")
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def ps(self):
|
def ps(self):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.ps()
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.ps()
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def port(self, service, private_port):
|
def port(self, service, private_port):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.port(service=service, private_port=private_port)
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.port(service=service, private_port=private_port)
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def execute(self, service, command, tty, envs):
|
def execute(self, service, command, tty, envs):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.execute(service=service, command=command, tty=tty, envs=envs)
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.execute(service=service, command=command, tty=tty, envs=envs)
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def logs(self, services, tail, follow, stream):
|
def logs(self, services, tail, follow, stream):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.compose.logs(services=services, tail=tail, follow=follow, stream=stream)
|
try:
|
||||||
except DockerException as e:
|
return self.docker.compose.logs(services=services, tail=tail, follow=follow, stream=stream)
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, ports=[], detach=False):
|
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, ports=[], detach=False):
|
||||||
try:
|
if not opts.o.dry_run:
|
||||||
return self.docker.run(image=image, command=command, user=user, volumes=volumes,
|
try:
|
||||||
entrypoint=entrypoint, envs=env, detach=detach, publish=ports, publish_all=len(ports) == 0)
|
return self.docker.run(image=image, command=command, user=user, volumes=volumes,
|
||||||
except DockerException as e:
|
entrypoint=entrypoint, envs=env, detach=detach, publish=ports, publish_all=len(ports) == 0)
|
||||||
raise DeployerException(e)
|
except DockerException as e:
|
||||||
|
raise DeployerException(e)
|
||||||
|
|
||||||
|
|
||||||
class DockerDeployerConfigGenerator(DeployerConfigGenerator):
|
class DockerDeployerConfigGenerator(DeployerConfigGenerator):
|
||||||
|
|||||||
@@ -85,54 +85,39 @@ def create_deploy_context(
|
|||||||
def up_operation(ctx, services_list, stay_attached=False):
|
def up_operation(ctx, services_list, stay_attached=False):
|
||||||
global_context = ctx.parent.parent.obj
|
global_context = ctx.parent.parent.obj
|
||||||
deploy_context = ctx.obj
|
deploy_context = ctx.obj
|
||||||
if not global_context.dry_run:
|
cluster_context = deploy_context.cluster_context
|
||||||
cluster_context = deploy_context.cluster_context
|
container_exec_env = _make_runtime_env(global_context)
|
||||||
container_exec_env = _make_runtime_env(global_context)
|
for attr, value in container_exec_env.items():
|
||||||
for attr, value in container_exec_env.items():
|
os.environ[attr] = value
|
||||||
os.environ[attr] = value
|
if global_context.verbose:
|
||||||
if global_context.verbose:
|
print(f"Running compose up with container_exec_env: {container_exec_env}, extra_args: {services_list}")
|
||||||
print(f"Running compose up with container_exec_env: {container_exec_env}, extra_args: {services_list}")
|
for pre_start_command in cluster_context.pre_start_commands:
|
||||||
for pre_start_command in cluster_context.pre_start_commands:
|
_run_command(global_context, cluster_context.cluster, pre_start_command)
|
||||||
_run_command(global_context, cluster_context.cluster, pre_start_command)
|
deploy_context.deployer.up(detach=not stay_attached, services=services_list)
|
||||||
deploy_context.deployer.up(detach=not stay_attached, services=services_list)
|
for post_start_command in cluster_context.post_start_commands:
|
||||||
for post_start_command in cluster_context.post_start_commands:
|
_run_command(global_context, cluster_context.cluster, post_start_command)
|
||||||
_run_command(global_context, cluster_context.cluster, post_start_command)
|
_orchestrate_cluster_config(global_context, cluster_context.config, deploy_context.deployer, container_exec_env)
|
||||||
_orchestrate_cluster_config(global_context, cluster_context.config, deploy_context.deployer, container_exec_env)
|
|
||||||
|
|
||||||
|
|
||||||
def down_operation(ctx, delete_volumes, extra_args_list):
|
def down_operation(ctx, delete_volumes, extra_args_list):
|
||||||
global_context = ctx.parent.parent.obj
|
timeout_arg = None
|
||||||
if not global_context.dry_run:
|
if extra_args_list:
|
||||||
if global_context.verbose:
|
timeout_arg = extra_args_list[0]
|
||||||
print("Running compose down")
|
# Specify shutdown timeout (default 10s) to give services enough time to shutdown gracefully
|
||||||
timeout_arg = None
|
ctx.obj.deployer.down(timeout=timeout_arg, volumes=delete_volumes)
|
||||||
if extra_args_list:
|
|
||||||
timeout_arg = extra_args_list[0]
|
|
||||||
# Specify shutdown timeout (default 10s) to give services enough time to shutdown gracefully
|
|
||||||
ctx.obj.deployer.down(timeout=timeout_arg, volumes=delete_volumes)
|
|
||||||
|
|
||||||
|
|
||||||
def status_operation(ctx):
|
def status_operation(ctx):
|
||||||
global_context = ctx.parent.parent.obj
|
ctx.obj.deployer.status()
|
||||||
if not global_context.dry_run:
|
|
||||||
if global_context.verbose:
|
|
||||||
print("Running compose status")
|
|
||||||
ctx.obj.deployer.status()
|
|
||||||
|
|
||||||
|
|
||||||
def update_operation(ctx):
|
def update_operation(ctx):
|
||||||
global_context = ctx.parent.parent.obj
|
ctx.obj.deployer.update()
|
||||||
if not global_context.dry_run:
|
|
||||||
if global_context.verbose:
|
|
||||||
print("Running compose update")
|
|
||||||
ctx.obj.deployer.update()
|
|
||||||
|
|
||||||
|
|
||||||
def ps_operation(ctx):
|
def ps_operation(ctx):
|
||||||
global_context = ctx.parent.parent.obj
|
global_context = ctx.parent.parent.obj
|
||||||
if not global_context.dry_run:
|
if not global_context.dry_run:
|
||||||
if global_context.verbose:
|
|
||||||
print("Running compose ps")
|
|
||||||
container_list = ctx.obj.deployer.ps()
|
container_list = ctx.obj.deployer.ps()
|
||||||
if len(container_list) > 0:
|
if len(container_list) > 0:
|
||||||
print("Running containers:")
|
print("Running containers:")
|
||||||
@@ -187,15 +172,11 @@ def exec_operation(ctx, extra_args):
|
|||||||
|
|
||||||
|
|
||||||
def logs_operation(ctx, tail: int, follow: bool, extra_args: str):
|
def logs_operation(ctx, tail: int, follow: bool, extra_args: str):
|
||||||
global_context = ctx.parent.parent.obj
|
|
||||||
extra_args_list = list(extra_args) or None
|
extra_args_list = list(extra_args) or None
|
||||||
if not global_context.dry_run:
|
services_list = extra_args_list if extra_args_list is not None else []
|
||||||
if global_context.verbose:
|
logs_stream = ctx.obj.deployer.logs(services=services_list, tail=tail, follow=follow, stream=True)
|
||||||
print("Running compose logs")
|
for stream_type, stream_content in logs_stream:
|
||||||
services_list = extra_args_list if extra_args_list is not None else []
|
print(stream_content.decode("utf-8"), end="")
|
||||||
logs_stream = ctx.obj.deployer.logs(services=services_list, tail=tail, follow=follow, stream=True)
|
|
||||||
for stream_type, stream_content in logs_stream:
|
|
||||||
print(stream_content.decode("utf-8"), end="")
|
|
||||||
|
|
||||||
|
|
||||||
@command.command()
|
@command.command()
|
||||||
@@ -347,8 +328,8 @@ def _make_cluster_context(ctx, stack, include, exclude, cluster, env_file):
|
|||||||
else:
|
else:
|
||||||
if deployment:
|
if deployment:
|
||||||
compose_file_name = os.path.join(compose_dir, f"docker-compose-{pod_name}.yml")
|
compose_file_name = os.path.join(compose_dir, f"docker-compose-{pod_name}.yml")
|
||||||
pod_pre_start_command = pod["pre_start_command"]
|
pod_pre_start_command = pod.get("pre_start_command")
|
||||||
pod_post_start_command = pod["post_start_command"]
|
pod_post_start_command = pod.get("post_start_command")
|
||||||
script_dir = compose_dir.parent.joinpath("pods", pod_name, "scripts")
|
script_dir = compose_dir.parent.joinpath("pods", pod_name, "scripts")
|
||||||
if pod_pre_start_command is not None:
|
if pod_pre_start_command is not None:
|
||||||
pre_start_commands.append(os.path.join(script_dir, pod_pre_start_command))
|
pre_start_commands.append(os.path.join(script_dir, pod_pre_start_command))
|
||||||
@@ -357,8 +338,8 @@ def _make_cluster_context(ctx, stack, include, exclude, cluster, env_file):
|
|||||||
else:
|
else:
|
||||||
pod_root_dir = os.path.join(dev_root_path, pod_repository.split("/")[-1], pod["path"])
|
pod_root_dir = os.path.join(dev_root_path, pod_repository.split("/")[-1], pod["path"])
|
||||||
compose_file_name = os.path.join(pod_root_dir, f"docker-compose-{pod_name}.yml")
|
compose_file_name = os.path.join(pod_root_dir, f"docker-compose-{pod_name}.yml")
|
||||||
pod_pre_start_command = pod["pre_start_command"]
|
pod_pre_start_command = pod.get("pre_start_command")
|
||||||
pod_post_start_command = pod["post_start_command"]
|
pod_post_start_command = pod.get("post_start_command")
|
||||||
if pod_pre_start_command is not None:
|
if pod_pre_start_command is not None:
|
||||||
pre_start_commands.append(os.path.join(pod_root_dir, pod_pre_start_command))
|
pre_start_commands.append(os.path.join(pod_root_dir, pod_pre_start_command))
|
||||||
if pod_post_start_command is not None:
|
if pod_post_start_command is not None:
|
||||||
@@ -463,7 +444,7 @@ def _orchestrate_cluster_config(ctx, cluster_config, deployer, container_exec_en
|
|||||||
tty=False,
|
tty=False,
|
||||||
envs=container_exec_env)
|
envs=container_exec_env)
|
||||||
waiting_for_data = False
|
waiting_for_data = False
|
||||||
if ctx.debug:
|
if ctx.debug and not waiting_for_data:
|
||||||
print(f"destination output: {destination_output}")
|
print(f"destination output: {destination_output}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ from stack_orchestrator.deploy.k8s.deploy_k8s import K8sDeployer, K8sDeployerCon
|
|||||||
from stack_orchestrator.deploy.compose.deploy_docker import DockerDeployer, DockerDeployerConfigGenerator
|
from stack_orchestrator.deploy.compose.deploy_docker import DockerDeployer, DockerDeployerConfigGenerator
|
||||||
|
|
||||||
|
|
||||||
def getDeployerConfigGenerator(type: str):
|
def getDeployerConfigGenerator(type: str, deployment_context):
|
||||||
if type == "compose" or type is None:
|
if type == "compose" or type is None:
|
||||||
return DockerDeployerConfigGenerator(type)
|
return DockerDeployerConfigGenerator(type)
|
||||||
elif type == constants.k8s_deploy_type or type == constants.k8s_kind_deploy_type:
|
elif type == constants.k8s_deploy_type or type == constants.k8s_kind_deploy_type:
|
||||||
return K8sDeployerConfigGenerator(type)
|
return K8sDeployerConfigGenerator(type, deployment_context)
|
||||||
else:
|
else:
|
||||||
print(f"ERROR: deploy-to {type} is not valid")
|
print(f"ERROR: deploy-to {type} is not valid")
|
||||||
|
|
||||||
|
|||||||
@@ -54,19 +54,44 @@ def _get_ports(stack):
|
|||||||
|
|
||||||
def _get_named_volumes(stack):
|
def _get_named_volumes(stack):
|
||||||
# Parse the compose files looking for named volumes
|
# Parse the compose files looking for named volumes
|
||||||
named_volumes = []
|
named_volumes = {
|
||||||
|
"rw": [],
|
||||||
|
"ro": []
|
||||||
|
}
|
||||||
parsed_stack = get_parsed_stack_config(stack)
|
parsed_stack = get_parsed_stack_config(stack)
|
||||||
pods = get_pod_list(parsed_stack)
|
pods = get_pod_list(parsed_stack)
|
||||||
yaml = get_yaml()
|
yaml = get_yaml()
|
||||||
|
|
||||||
|
def find_vol_usage(parsed_pod_file, vol):
|
||||||
|
ret = {}
|
||||||
|
if "services" in parsed_pod_file:
|
||||||
|
for svc_name, svc in parsed_pod_file["services"].items():
|
||||||
|
if "volumes" in svc:
|
||||||
|
for svc_volume in svc["volumes"]:
|
||||||
|
parts = svc_volume.split(":")
|
||||||
|
if parts[0] == vol:
|
||||||
|
ret[svc_name] = {
|
||||||
|
"volume": parts[0],
|
||||||
|
"mount": parts[1],
|
||||||
|
"options": parts[2] if len(parts) == 3 else None
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
|
||||||
for pod in pods:
|
for pod in pods:
|
||||||
pod_file_path = get_pod_file_path(parsed_stack, pod)
|
pod_file_path = get_pod_file_path(parsed_stack, pod)
|
||||||
parsed_pod_file = yaml.load(open(pod_file_path, "r"))
|
parsed_pod_file = yaml.load(open(pod_file_path, "r"))
|
||||||
if "volumes" in parsed_pod_file:
|
if "volumes" in parsed_pod_file:
|
||||||
volumes = parsed_pod_file["volumes"]
|
volumes = parsed_pod_file["volumes"]
|
||||||
for volume in volumes.keys():
|
for volume in volumes.keys():
|
||||||
# Volume definition looks like:
|
for vu in find_vol_usage(parsed_pod_file, volume).values():
|
||||||
# 'laconicd-data': None
|
read_only = vu["options"] == "ro"
|
||||||
named_volumes.append(volume)
|
if read_only:
|
||||||
|
if vu["volume"] not in named_volumes["rw"] and vu["volume"] not in named_volumes["ro"]:
|
||||||
|
named_volumes["ro"].append(vu["volume"])
|
||||||
|
else:
|
||||||
|
if vu["volume"] not in named_volumes["rw"]:
|
||||||
|
named_volumes["rw"].append(vu["volume"])
|
||||||
|
|
||||||
return named_volumes
|
return named_volumes
|
||||||
|
|
||||||
|
|
||||||
@@ -98,12 +123,24 @@ def _fixup_pod_file(pod, spec, compose_dir):
|
|||||||
_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",
|
new_volume_spec = {"driver": "local",
|
||||||
"driver_opts": {
|
"driver_opts": {
|
||||||
"type": "none",
|
"type": "none",
|
||||||
"device": volume_spec_fixedup,
|
"device": volume_spec_fixedup,
|
||||||
"o": "bind"
|
"o": "bind"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pod["volumes"][volume] = new_volume_spec
|
pod["volumes"][volume] = new_volume_spec
|
||||||
|
|
||||||
|
# Fix up configmaps
|
||||||
|
if "configmaps" in spec:
|
||||||
|
spec_cfgmaps = spec["configmaps"]
|
||||||
|
if "volumes" in pod:
|
||||||
|
pod_volumes = pod["volumes"]
|
||||||
|
for volume in pod_volumes.keys():
|
||||||
|
if volume in spec_cfgmaps:
|
||||||
|
volume_cfg = spec_cfgmaps[volume]
|
||||||
|
# Just make the dir (if necessary)
|
||||||
|
_create_bind_dir_if_relative(volume, volume_cfg, compose_dir)
|
||||||
|
|
||||||
# Fix up ports
|
# Fix up ports
|
||||||
if "network" in spec and "ports" in spec["network"]:
|
if "network" in spec and "ports" in spec["network"]:
|
||||||
spec_ports = spec["network"]["ports"]
|
spec_ports = spec["network"]["ports"]
|
||||||
@@ -319,9 +356,18 @@ def init_operation(deploy_command_context, stack, deployer_type, config,
|
|||||||
named_volumes = _get_named_volumes(stack)
|
named_volumes = _get_named_volumes(stack)
|
||||||
if named_volumes:
|
if named_volumes:
|
||||||
volume_descriptors = {}
|
volume_descriptors = {}
|
||||||
for named_volume in named_volumes:
|
configmap_descriptors = {}
|
||||||
|
for named_volume in named_volumes["rw"]:
|
||||||
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
spec_file_content["volumes"] = volume_descriptors
|
for named_volume in named_volumes["ro"]:
|
||||||
|
if "k8s" in deployer_type and "config" in named_volume:
|
||||||
|
configmap_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
|
else:
|
||||||
|
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
|
if volume_descriptors:
|
||||||
|
spec_file_content["volumes"] = volume_descriptors
|
||||||
|
if configmap_descriptors:
|
||||||
|
spec_file_content["configmaps"] = configmap_descriptors
|
||||||
|
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Creating spec file for stack: {stack} with content: {spec_file_content}")
|
print(f"Creating spec file for stack: {stack} with content: {spec_file_content}")
|
||||||
@@ -441,7 +487,7 @@ def create_operation(deployment_command_context, spec_file, deployment_dir, netw
|
|||||||
deployment_context = DeploymentContext()
|
deployment_context = DeploymentContext()
|
||||||
deployment_context.init(deployment_dir_path)
|
deployment_context.init(deployment_dir_path)
|
||||||
# Call the deployer to generate any deployer-specific files (e.g. for kind)
|
# Call the deployer to generate any deployer-specific files (e.g. for kind)
|
||||||
deployer_config_generator = getDeployerConfigGenerator(deployment_type)
|
deployer_config_generator = getDeployerConfigGenerator(deployment_type, deployment_context)
|
||||||
# TODO: make deployment_dir_path a Path above
|
# TODO: make deployment_dir_path a Path above
|
||||||
deployer_config_generator.generate(deployment_dir_path)
|
deployer_config_generator.generate(deployment_dir_path)
|
||||||
call_stack_deploy_create(deployment_context, [network_dir, initial_peers, deployment_command_context])
|
call_stack_deploy_create(deployment_context, [network_dir, initial_peers, deployment_command_context])
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ def _image_needs_pushed(image: str):
|
|||||||
|
|
||||||
def remote_tag_for_image(image: str, remote_repo_url: str):
|
def remote_tag_for_image(image: str, remote_repo_url: str):
|
||||||
# Turns image tags of the form: foo/bar:local into remote.repo/org/bar:deploy
|
# Turns image tags of the form: foo/bar:local into remote.repo/org/bar:deploy
|
||||||
(org, image_name_with_version) = image.split("/")
|
major_parts = image.split("/", 2)
|
||||||
|
image_name_with_version = major_parts[1] if 2 == len(major_parts) else major_parts[0]
|
||||||
(image_name, image_version) = image_name_with_version.split(":")
|
(image_name, image_version) = image_name_with_version.split(":")
|
||||||
if image_version == "local":
|
if image_version == "local":
|
||||||
return f"{remote_repo_url}/{image_name}:deploy"
|
return f"{remote_repo_url}/{image_name}:deploy"
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
# 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/>.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from kubernetes import client
|
from kubernetes import client
|
||||||
from typing import Any, List, Set
|
from typing import Any, List, Set
|
||||||
|
|
||||||
@@ -112,9 +114,10 @@ class ClusterInfo:
|
|||||||
services = pod["services"]
|
services = pod["services"]
|
||||||
for service_name in services:
|
for service_name in services:
|
||||||
service_info = services[service_name]
|
service_info = services[service_name]
|
||||||
port = int(service_info["ports"][0])
|
if "ports" in service_info:
|
||||||
if opts.o.debug:
|
port = int(service_info["ports"][0])
|
||||||
print(f"service port: {port}")
|
if opts.o.debug:
|
||||||
|
print(f"service port: {port}")
|
||||||
service = client.V1Service(
|
service = client.V1Service(
|
||||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-service"),
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-service"),
|
||||||
spec=client.V1ServiceSpec(
|
spec=client.V1ServiceSpec(
|
||||||
@@ -130,30 +133,70 @@ class ClusterInfo:
|
|||||||
|
|
||||||
def get_pvcs(self):
|
def get_pvcs(self):
|
||||||
result = []
|
result = []
|
||||||
volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
spec_volumes = self.spec.get_volumes()
|
||||||
|
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Volumes: {volumes}")
|
print(f"Spec Volumes: {spec_volumes}")
|
||||||
for volume_name in volumes:
|
print(f"Named Volumes: {named_volumes}")
|
||||||
|
for volume_name in spec_volumes:
|
||||||
|
if volume_name not in named_volumes:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"{volume_name} not in pod files")
|
||||||
|
continue
|
||||||
spec = client.V1PersistentVolumeClaimSpec(
|
spec = client.V1PersistentVolumeClaimSpec(
|
||||||
access_modes=["ReadWriteOnce"],
|
access_modes=["ReadWriteOnce"],
|
||||||
storage_class_name="manual",
|
storage_class_name="manual",
|
||||||
resources=client.V1ResourceRequirements(
|
resources=client.V1ResourceRequirements(
|
||||||
requests={"storage": "2Gi"}
|
requests={"storage": "2Gi"}
|
||||||
),
|
),
|
||||||
volume_name=volume_name
|
volume_name=f"{self.app_name}-{volume_name}"
|
||||||
)
|
)
|
||||||
pvc = client.V1PersistentVolumeClaim(
|
pvc = client.V1PersistentVolumeClaim(
|
||||||
metadata=client.V1ObjectMeta(name=volume_name,
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
||||||
labels={"volume-label": volume_name}),
|
labels={"volume-label": f"{self.app_name}-{volume_name}"}),
|
||||||
spec=spec,
|
spec=spec,
|
||||||
)
|
)
|
||||||
result.append(pvc)
|
result.append(pvc)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_configmaps(self):
|
||||||
|
result = []
|
||||||
|
spec_configmaps = self.spec.get_configmaps()
|
||||||
|
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
||||||
|
for cfg_map_name, cfg_map_path in spec_configmaps.items():
|
||||||
|
if cfg_map_name not in named_volumes:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"{cfg_map_name} not in pod files")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not cfg_map_path.startswith("/"):
|
||||||
|
cfg_map_path = os.path.join(os.path.dirname(self.spec.file_path), cfg_map_path)
|
||||||
|
|
||||||
|
# Read in all the files at a single-level of the directory. This mimics the behavior
|
||||||
|
# of `kubectl create configmap foo --from-file=/path/to/dir`
|
||||||
|
data = {}
|
||||||
|
for f in os.listdir(cfg_map_path):
|
||||||
|
full_path = os.path.join(cfg_map_path, f)
|
||||||
|
if os.path.isfile(full_path):
|
||||||
|
data[f] = open(full_path, 'rt').read()
|
||||||
|
|
||||||
|
spec = client.V1ConfigMap(
|
||||||
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{cfg_map_name}",
|
||||||
|
labels={"configmap-label": cfg_map_name}),
|
||||||
|
data=data
|
||||||
|
)
|
||||||
|
result.append(spec)
|
||||||
|
return result
|
||||||
|
|
||||||
def get_pvs(self):
|
def get_pvs(self):
|
||||||
result = []
|
result = []
|
||||||
volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
spec_volumes = self.spec.get_volumes()
|
||||||
for volume_name in volumes:
|
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
||||||
|
for volume_name in spec_volumes:
|
||||||
|
if volume_name not in named_volumes:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"{volume_name} not in pod files")
|
||||||
|
continue
|
||||||
spec = client.V1PersistentVolumeSpec(
|
spec = client.V1PersistentVolumeSpec(
|
||||||
storage_class_name="manual",
|
storage_class_name="manual",
|
||||||
access_modes=["ReadWriteOnce"],
|
access_modes=["ReadWriteOnce"],
|
||||||
@@ -161,8 +204,8 @@ class ClusterInfo:
|
|||||||
host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name))
|
host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name))
|
||||||
)
|
)
|
||||||
pv = client.V1PersistentVolume(
|
pv = client.V1PersistentVolume(
|
||||||
metadata=client.V1ObjectMeta(name=volume_name,
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
||||||
labels={"volume-label": volume_name}),
|
labels={"volume-label": f"{self.app_name}-{volume_name}"}),
|
||||||
spec=spec,
|
spec=spec,
|
||||||
)
|
)
|
||||||
result.append(pv)
|
result.append(pv)
|
||||||
@@ -178,10 +221,11 @@ class ClusterInfo:
|
|||||||
container_name = service_name
|
container_name = service_name
|
||||||
service_info = services[service_name]
|
service_info = services[service_name]
|
||||||
image = service_info["image"]
|
image = service_info["image"]
|
||||||
port = int(service_info["ports"][0])
|
if "ports" in service_info:
|
||||||
if opts.o.debug:
|
port = int(service_info["ports"][0])
|
||||||
print(f"image: {image}")
|
if opts.o.debug:
|
||||||
print(f"service port: {port}")
|
print(f"image: {image}")
|
||||||
|
print(f"service port: {port}")
|
||||||
# Re-write the image tag for remote deployment
|
# Re-write the image tag for remote deployment
|
||||||
image_to_use = remote_tag_for_image(
|
image_to_use = remote_tag_for_image(
|
||||||
image, self.spec.get_image_registry()) if self.spec.get_image_registry() is not None else image
|
image, self.spec.get_image_registry()) if self.spec.get_image_registry() is not None else image
|
||||||
@@ -195,11 +239,11 @@ class ClusterInfo:
|
|||||||
volume_mounts=volume_mounts,
|
volume_mounts=volume_mounts,
|
||||||
resources=client.V1ResourceRequirements(
|
resources=client.V1ResourceRequirements(
|
||||||
requests={"cpu": "100m", "memory": "200Mi"},
|
requests={"cpu": "100m", "memory": "200Mi"},
|
||||||
limits={"cpu": "500m", "memory": "500Mi"},
|
limits={"cpu": "1000m", "memory": "2000Mi"},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
containers.append(container)
|
containers.append(container)
|
||||||
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map)
|
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")]
|
||||||
template = client.V1PodTemplateSpec(
|
template = client.V1PodTemplateSpec(
|
||||||
metadata=client.V1ObjectMeta(labels={"app": self.app_name}),
|
metadata=client.V1ObjectMeta(labels={"app": self.app_name}),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# Copyright © 2023 Vulcanize
|
# Copyright © 2023 Vulcanize
|
||||||
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
@@ -82,70 +81,104 @@ class K8sDeployer(Deployer):
|
|||||||
self.apps_api = client.AppsV1Api()
|
self.apps_api = client.AppsV1Api()
|
||||||
self.custom_obj_api = client.CustomObjectsApi()
|
self.custom_obj_api = client.CustomObjectsApi()
|
||||||
|
|
||||||
def up(self, detach, services):
|
def _create_volume_data(self):
|
||||||
|
|
||||||
if self.is_kind():
|
|
||||||
# Create the kind cluster
|
|
||||||
create_cluster(self.kind_cluster_name, self.deployment_dir.joinpath(constants.kind_config_filename))
|
|
||||||
# Ensure the referenced containers are copied into kind
|
|
||||||
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
|
||||||
self.connect_api()
|
|
||||||
|
|
||||||
# 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:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this pv: {pv}")
|
print(f"Sending this pv: {pv}")
|
||||||
pv_resp = self.core_api.create_persistent_volume(body=pv)
|
if not opts.o.dry_run:
|
||||||
if opts.o.debug:
|
pv_resp = self.core_api.create_persistent_volume(body=pv)
|
||||||
print("PVs created:")
|
if opts.o.debug:
|
||||||
print(f"{pv_resp}")
|
print("PVs created:")
|
||||||
|
print(f"{pv_resp}")
|
||||||
|
|
||||||
# Figure out the PVCs for this deployment
|
# Figure out the PVCs for this deployment
|
||||||
pvcs = self.cluster_info.get_pvcs()
|
pvcs = self.cluster_info.get_pvcs()
|
||||||
for pvc in pvcs:
|
for pvc in pvcs:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this pvc: {pvc}")
|
print(f"Sending this pvc: {pvc}")
|
||||||
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
|
||||||
|
if not opts.o.dry_run:
|
||||||
|
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVCs created:")
|
||||||
|
print(f"{pvc_resp}")
|
||||||
|
|
||||||
|
# Figure out the ConfigMaps for this deployment
|
||||||
|
config_maps = self.cluster_info.get_configmaps()
|
||||||
|
for cfg_map in config_maps:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("PVCs created:")
|
print(f"Sending this ConfigMap: {cfg_map}")
|
||||||
print(f"{pvc_resp}")
|
if not opts.o.dry_run:
|
||||||
|
cfg_rsp = self.core_api.create_namespaced_config_map(
|
||||||
|
body=cfg_map,
|
||||||
|
namespace=self.k8s_namespace
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("ConfigMap created:")
|
||||||
|
print(f"{cfg_rsp}")
|
||||||
|
|
||||||
|
def _create_deployment(self):
|
||||||
# Process compose files into a Deployment
|
# Process compose files into a Deployment
|
||||||
deployment = self.cluster_info.get_deployment(image_pull_policy=None if self.is_kind() else "Always")
|
deployment = self.cluster_info.get_deployment(image_pull_policy=None if self.is_kind() else "Always")
|
||||||
# Create the k8s objects
|
# Create the k8s objects
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this deployment: {deployment}")
|
print(f"Sending this deployment: {deployment}")
|
||||||
deployment_resp = self.apps_api.create_namespaced_deployment(
|
if not opts.o.dry_run:
|
||||||
body=deployment, namespace=self.k8s_namespace
|
deployment_resp = self.apps_api.create_namespaced_deployment(
|
||||||
)
|
body=deployment, namespace=self.k8s_namespace
|
||||||
if opts.o.debug:
|
)
|
||||||
print("Deployment created:")
|
if opts.o.debug:
|
||||||
print(f"{deployment_resp.metadata.namespace} {deployment_resp.metadata.name} \
|
print("Deployment created:")
|
||||||
{deployment_resp.metadata.generation} {deployment_resp.spec.template.spec.containers[0].image}")
|
print(f"{deployment_resp.metadata.namespace} {deployment_resp.metadata.name} \
|
||||||
|
{deployment_resp.metadata.generation} {deployment_resp.spec.template.spec.containers[0].image}")
|
||||||
|
|
||||||
service: client.V1Service = self.cluster_info.get_service()
|
service: client.V1Service = self.cluster_info.get_service()
|
||||||
service_resp = self.core_api.create_namespaced_service(
|
|
||||||
namespace=self.k8s_namespace,
|
|
||||||
body=service
|
|
||||||
)
|
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("Service created:")
|
print(f"Sending this service: {service}")
|
||||||
print(f"{service_resp}")
|
if not opts.o.dry_run:
|
||||||
|
service_resp = self.core_api.create_namespaced_service(
|
||||||
|
namespace=self.k8s_namespace,
|
||||||
|
body=service
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("Service created:")
|
||||||
|
print(f"{service_resp}")
|
||||||
|
|
||||||
|
def up(self, detach, services):
|
||||||
|
if not opts.o.dry_run:
|
||||||
|
if self.is_kind():
|
||||||
|
# Create the kind cluster
|
||||||
|
create_cluster(self.kind_cluster_name, self.deployment_dir.joinpath(constants.kind_config_filename))
|
||||||
|
# Ensure the referenced containers are copied into kind
|
||||||
|
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
||||||
|
self.connect_api()
|
||||||
|
else:
|
||||||
|
print("Dry run mode enabled, skipping k8s API connect")
|
||||||
|
|
||||||
|
self._create_volume_data()
|
||||||
|
self._create_deployment()
|
||||||
|
|
||||||
if not self.is_kind():
|
if not self.is_kind():
|
||||||
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
||||||
|
|
||||||
if opts.o.debug:
|
if ingress:
|
||||||
print(f"Sending this ingress: {ingress}")
|
if opts.o.debug:
|
||||||
ingress_resp = self.networking_api.create_namespaced_ingress(
|
print(f"Sending this ingress: {ingress}")
|
||||||
namespace=self.k8s_namespace,
|
if not opts.o.dry_run:
|
||||||
body=ingress
|
ingress_resp = self.networking_api.create_namespaced_ingress(
|
||||||
)
|
namespace=self.k8s_namespace,
|
||||||
if opts.o.debug:
|
body=ingress
|
||||||
print("Ingress created:")
|
)
|
||||||
print(f"{ingress_resp}")
|
if opts.o.debug:
|
||||||
|
print("Ingress created:")
|
||||||
|
print(f"{ingress_resp}")
|
||||||
|
else:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("No ingress configured")
|
||||||
|
|
||||||
def down(self, timeout, volumes):
|
def down(self, timeout, volumes): # noqa: C901
|
||||||
self.connect_api()
|
self.connect_api()
|
||||||
# Delete the k8s objects
|
# Delete the k8s objects
|
||||||
# Create the host-path-mounted PVs for this deployment
|
# Create the host-path-mounted PVs for this deployment
|
||||||
@@ -175,6 +208,22 @@ class K8sDeployer(Deployer):
|
|||||||
print(f"{pvc_resp}")
|
print(f"{pvc_resp}")
|
||||||
except client.exceptions.ApiException as e:
|
except client.exceptions.ApiException as e:
|
||||||
_check_delete_exception(e)
|
_check_delete_exception(e)
|
||||||
|
|
||||||
|
# Figure out the ConfigMaps for this deployment
|
||||||
|
cfg_maps = self.cluster_info.get_configmaps()
|
||||||
|
for cfg_map in cfg_maps:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Deleting this ConfigMap: {cfg_map}")
|
||||||
|
try:
|
||||||
|
cfg_map_resp = self.core_api.delete_namespaced_config_map(
|
||||||
|
name=cfg_map.metadata.name, namespace=self.k8s_namespace
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("ConfigMap deleted:")
|
||||||
|
print(f"{cfg_map_resp}")
|
||||||
|
except client.exceptions.ApiException as e:
|
||||||
|
_check_delete_exception(e)
|
||||||
|
|
||||||
deployment = self.cluster_info.get_deployment()
|
deployment = self.cluster_info.get_deployment()
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Deleting this deployment: {deployment}")
|
print(f"Deleting this deployment: {deployment}")
|
||||||
@@ -198,14 +247,18 @@ class K8sDeployer(Deployer):
|
|||||||
|
|
||||||
if not self.is_kind():
|
if not self.is_kind():
|
||||||
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
||||||
if opts.o.debug:
|
if ingress:
|
||||||
print(f"Deleting this ingress: {ingress}")
|
if opts.o.debug:
|
||||||
try:
|
print(f"Deleting this ingress: {ingress}")
|
||||||
self.networking_api.delete_namespaced_ingress(
|
try:
|
||||||
name=ingress.metadata.name, namespace=self.k8s_namespace
|
self.networking_api.delete_namespaced_ingress(
|
||||||
)
|
name=ingress.metadata.name, namespace=self.k8s_namespace
|
||||||
except client.exceptions.ApiException as e:
|
)
|
||||||
_check_delete_exception(e)
|
except client.exceptions.ApiException as e:
|
||||||
|
_check_delete_exception(e)
|
||||||
|
else:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("No ingress to delete")
|
||||||
|
|
||||||
if self.is_kind():
|
if self.is_kind():
|
||||||
# Destroy the kind cluster
|
# Destroy the kind cluster
|
||||||
@@ -353,8 +406,9 @@ class K8sDeployer(Deployer):
|
|||||||
class K8sDeployerConfigGenerator(DeployerConfigGenerator):
|
class K8sDeployerConfigGenerator(DeployerConfigGenerator):
|
||||||
type: str
|
type: str
|
||||||
|
|
||||||
def __init__(self, type: str) -> None:
|
def __init__(self, type: str, deployment_context) -> None:
|
||||||
self.type = type
|
self.type = type
|
||||||
|
self.deployment_context = deployment_context
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def generate(self, deployment_dir: Path):
|
def generate(self, deployment_dir: Path):
|
||||||
@@ -362,7 +416,7 @@ class K8sDeployerConfigGenerator(DeployerConfigGenerator):
|
|||||||
if self.type == "k8s-kind":
|
if self.type == "k8s-kind":
|
||||||
# Check the file isn't already there
|
# Check the file isn't already there
|
||||||
# Get the config file contents
|
# Get the config file contents
|
||||||
content = generate_kind_config(deployment_dir)
|
content = generate_kind_config(deployment_dir, self.deployment_context)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"kind config is: {content}")
|
print(f"kind config is: {content}")
|
||||||
config_file = deployment_dir.joinpath(constants.kind_config_filename)
|
config_file = deployment_dir.joinpath(constants.kind_config_filename)
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ def named_volumes_from_pod_files(parsed_pod_files):
|
|||||||
parsed_pod_file = parsed_pod_files[pod]
|
parsed_pod_file = parsed_pod_files[pod]
|
||||||
if "volumes" in parsed_pod_file:
|
if "volumes" in parsed_pod_file:
|
||||||
volumes = parsed_pod_file["volumes"]
|
volumes = parsed_pod_file["volumes"]
|
||||||
for volume in volumes.keys():
|
for volume, value in volumes.items():
|
||||||
# Volume definition looks like:
|
# Volume definition looks like:
|
||||||
# 'laconicd-data': None
|
# 'laconicd-data': None
|
||||||
named_volumes.append(volume)
|
named_volumes.append(volume)
|
||||||
@@ -97,23 +97,38 @@ def volume_mounts_for_service(parsed_pod_files, service):
|
|||||||
if "volumes" in service_obj:
|
if "volumes" in service_obj:
|
||||||
volumes = service_obj["volumes"]
|
volumes = service_obj["volumes"]
|
||||||
for mount_string in volumes:
|
for mount_string in volumes:
|
||||||
# Looks like: test-data:/data
|
# Looks like: test-data:/data or test-data:/data:ro or test-data:/data:rw
|
||||||
(volume_name, mount_path) = mount_string.split(":")
|
if opts.o.debug:
|
||||||
volume_device = client.V1VolumeMount(mount_path=mount_path, name=volume_name)
|
print(f"mount_string: {mount_string}")
|
||||||
|
mount_split = mount_string.split(":")
|
||||||
|
volume_name = mount_split[0]
|
||||||
|
mount_path = mount_split[1]
|
||||||
|
mount_options = mount_split[2] if len(mount_split) == 3 else None
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"volumne_name: {volume_name}")
|
||||||
|
print(f"mount path: {mount_path}")
|
||||||
|
print(f"mount options: {mount_options}")
|
||||||
|
volume_device = client.V1VolumeMount(
|
||||||
|
mount_path=mount_path, name=volume_name, read_only="ro" == mount_options)
|
||||||
result.append(volume_device)
|
result.append(volume_device)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def volumes_for_pod_files(parsed_pod_files):
|
def volumes_for_pod_files(parsed_pod_files, spec, app_name):
|
||||||
result = []
|
result = []
|
||||||
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]
|
||||||
if "volumes" in parsed_pod_file:
|
if "volumes" in parsed_pod_file:
|
||||||
volumes = parsed_pod_file["volumes"]
|
volumes = parsed_pod_file["volumes"]
|
||||||
for volume_name in volumes.keys():
|
for volume_name in volumes.keys():
|
||||||
claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name)
|
if volume_name in spec.get_configmaps():
|
||||||
volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim)
|
config_map = client.V1ConfigMapVolumeSource(name=f"{app_name}-{volume_name}")
|
||||||
result.append(volume)
|
volume = client.V1Volume(name=volume_name, config_map=config_map)
|
||||||
|
result.append(volume)
|
||||||
|
else:
|
||||||
|
claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=f"{app_name}-{volume_name}")
|
||||||
|
volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim)
|
||||||
|
result.append(volume)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -125,8 +140,9 @@ def _get_host_paths_for_volumes(parsed_pod_files):
|
|||||||
volumes = parsed_pod_file["volumes"]
|
volumes = parsed_pod_file["volumes"]
|
||||||
for volume_name in volumes.keys():
|
for volume_name in volumes.keys():
|
||||||
volume_definition = volumes[volume_name]
|
volume_definition = volumes[volume_name]
|
||||||
host_path = volume_definition["driver_opts"]["device"]
|
if volume_definition and "driver_opts" in volume_definition:
|
||||||
result[volume_name] = host_path
|
host_path = volume_definition["driver_opts"]["device"]
|
||||||
|
result[volume_name] = host_path
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -138,7 +154,7 @@ def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Pat
|
|||||||
return Path.cwd().joinpath(deployment_dir.joinpath("compose").joinpath(data_mount_path)).resolve()
|
return Path.cwd().joinpath(deployment_dir.joinpath("compose").joinpath(data_mount_path)).resolve()
|
||||||
|
|
||||||
|
|
||||||
def _generate_kind_mounts(parsed_pod_files, deployment_dir):
|
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(parsed_pod_files)
|
||||||
# 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)
|
||||||
@@ -153,11 +169,20 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir):
|
|||||||
if "volumes" in service_obj:
|
if "volumes" in service_obj:
|
||||||
volumes = service_obj["volumes"]
|
volumes = service_obj["volumes"]
|
||||||
for mount_string in volumes:
|
for mount_string in volumes:
|
||||||
# Looks like: test-data:/data
|
# Looks like: test-data:/data or test-data:/data:ro or test-data:/data:rw
|
||||||
(volume_name, mount_path) = mount_string.split(":")
|
if opts.o.debug:
|
||||||
volume_definitions.append(
|
print(f"mount_string: {mount_string}")
|
||||||
f" - hostPath: {_make_absolute_host_path(volume_host_path_map[volume_name], deployment_dir)}\n"
|
mount_split = mount_string.split(":")
|
||||||
f" containerPath: {get_node_pv_mount_path(volume_name)}"
|
volume_name = mount_split[0]
|
||||||
|
mount_path = mount_split[1]
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"volumne_name: {volume_name}")
|
||||||
|
print(f"map: {volume_host_path_map}")
|
||||||
|
print(f"mount path: {mount_path}")
|
||||||
|
if volume_name not in deployment_context.spec.get_configmaps():
|
||||||
|
volume_definitions.append(
|
||||||
|
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"
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
"" if len(volume_definitions) == 0 else (
|
"" if len(volume_definitions) == 0 else (
|
||||||
@@ -180,7 +205,7 @@ def _generate_kind_port_mappings(parsed_pod_files):
|
|||||||
for port_string in ports:
|
for port_string in ports:
|
||||||
# TODO handle the complex cases
|
# TODO handle the complex cases
|
||||||
# Looks like: 80 or something more complicated
|
# Looks like: 80 or something more complicated
|
||||||
port_definitions.append(f" - containerPort: {port_string}\n hostPort: {port_string}")
|
port_definitions.append(f" - containerPort: {port_string}\n hostPort: {port_string}\n")
|
||||||
return (
|
return (
|
||||||
"" if len(port_definitions) == 0 else (
|
"" if len(port_definitions) == 0 else (
|
||||||
" extraPortMappings:\n"
|
" extraPortMappings:\n"
|
||||||
@@ -214,13 +239,13 @@ def envs_from_environment_variables_map(map: Mapping[str, str]) -> List[client.V
|
|||||||
# extraMounts:
|
# extraMounts:
|
||||||
# - hostPath: /path/to/my/files
|
# - hostPath: /path/to/my/files
|
||||||
# containerPath: /files
|
# containerPath: /files
|
||||||
def generate_kind_config(deployment_dir: Path):
|
def generate_kind_config(deployment_dir: Path, deployment_context):
|
||||||
compose_file_dir = deployment_dir.joinpath("compose")
|
compose_file_dir = deployment_dir.joinpath("compose")
|
||||||
# TODO: this should come from the stack file, not this way
|
# TODO: this should come from the stack file, not this way
|
||||||
pod_files = [p for p in compose_file_dir.iterdir() if p.is_file()]
|
pod_files = [p for p in compose_file_dir.iterdir() if p.is_file()]
|
||||||
parsed_pod_files_map = parsed_pod_files_map_from_file_names(pod_files)
|
parsed_pod_files_map = parsed_pod_files_map_from_file_names(pod_files)
|
||||||
port_mappings_yml = _generate_kind_port_mappings(parsed_pod_files_map)
|
port_mappings_yml = _generate_kind_port_mappings(parsed_pod_files_map)
|
||||||
mounts_yml = _generate_kind_mounts(parsed_pod_files_map, deployment_dir)
|
mounts_yml = _generate_kind_mounts(parsed_pod_files_map, deployment_dir, deployment_context)
|
||||||
return (
|
return (
|
||||||
"kind: Cluster\n"
|
"kind: Cluster\n"
|
||||||
"apiVersion: kind.x-k8s.io/v1alpha4\n"
|
"apiVersion: kind.x-k8s.io/v1alpha4\n"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from stack_orchestrator import constants
|
|||||||
class Spec:
|
class Spec:
|
||||||
|
|
||||||
obj: typing.Any
|
obj: typing.Any
|
||||||
|
file_path: Path
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
pass
|
pass
|
||||||
@@ -29,12 +30,23 @@ class Spec:
|
|||||||
def init_from_file(self, file_path: Path):
|
def init_from_file(self, file_path: Path):
|
||||||
with file_path:
|
with file_path:
|
||||||
self.obj = get_yaml().load(open(file_path, "r"))
|
self.obj = get_yaml().load(open(file_path, "r"))
|
||||||
|
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_resigtry_key]
|
||||||
if self.obj and constants.image_resigtry_key in self.obj
|
if self.obj and constants.image_resigtry_key in self.obj
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
|
def get_volumes(self):
|
||||||
|
return (self.obj["volumes"]
|
||||||
|
if self.obj and "volumes" in self.obj
|
||||||
|
else {})
|
||||||
|
|
||||||
|
def get_configmaps(self):
|
||||||
|
return (self.obj["configmaps"]
|
||||||
|
if self.obj and "configmaps" in self.obj
|
||||||
|
else {})
|
||||||
|
|
||||||
def get_http_proxy(self):
|
def get_http_proxy(self):
|
||||||
return (self.obj[constants.network_key][constants.http_proxy_key]
|
return (self.obj[constants.network_key][constants.http_proxy_key]
|
||||||
if self.obj and constants.network_key in self.obj
|
if self.obj and constants.network_key in self.obj
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ def process_app_deployment_request(
|
|||||||
dns_record = laconic.get_record(dns_crn)
|
dns_record = laconic.get_record(dns_crn)
|
||||||
if dns_record:
|
if dns_record:
|
||||||
matched_owner = match_owner(app_deployment_request, dns_record)
|
matched_owner = match_owner(app_deployment_request, dns_record)
|
||||||
if not matched_owner and dns_record.request:
|
if not matched_owner and dns_record.attributes.request:
|
||||||
matched_owner = match_owner(app_deployment_request, laconic.get_record(dns_record.request, require=True))
|
matched_owner = match_owner(app_deployment_request, laconic.get_record(dns_record.attributes.request, require=True))
|
||||||
|
|
||||||
if matched_owner:
|
if matched_owner:
|
||||||
print("Matched DnsRecord ownership:", matched_owner)
|
print("Matched DnsRecord ownership:", matched_owner)
|
||||||
@@ -136,13 +136,17 @@ def load_known_requests(filename):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def dump_known_requests(filename, requests):
|
def dump_known_requests(filename, requests, status="SEEN"):
|
||||||
if not filename:
|
if not filename:
|
||||||
return
|
return
|
||||||
known_requests = load_known_requests(filename)
|
known_requests = load_known_requests(filename)
|
||||||
for r in requests:
|
for r in requests:
|
||||||
known_requests[r.id] = r.createTime
|
known_requests[r.id] = {
|
||||||
json.dump(known_requests, open(filename, "w"))
|
"createTime": r.createTime,
|
||||||
|
"status": status
|
||||||
|
}
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
json.dump(known_requests, f)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@@ -201,6 +205,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
requests.reverse()
|
requests.reverse()
|
||||||
requests_by_name = {}
|
requests_by_name = {}
|
||||||
for r in requests:
|
for r in requests:
|
||||||
|
# TODO: Do this _after_ filtering deployments and cancellations to minimize round trips.
|
||||||
app = laconic.get_record(r.attributes.application)
|
app = laconic.get_record(r.attributes.application)
|
||||||
if not app:
|
if not app:
|
||||||
print("Skipping request %s, cannot locate app." % r.id)
|
print("Skipping request %s, cannot locate app." % r.id)
|
||||||
@@ -256,6 +261,8 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
|
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
for r in requests_to_execute:
|
for r in requests_to_execute:
|
||||||
|
dump_known_requests(state_file, [r], "DEPLOYING")
|
||||||
|
status = "ERROR"
|
||||||
try:
|
try:
|
||||||
process_app_deployment_request(
|
process_app_deployment_request(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -268,5 +275,6 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
kube_config,
|
kube_config,
|
||||||
image_registry
|
image_registry
|
||||||
)
|
)
|
||||||
|
status = "DEPLOYED"
|
||||||
finally:
|
finally:
|
||||||
dump_known_requests(state_file, [r])
|
dump_known_requests(state_file, [r], status)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from dotenv import dotenv_values
|
|||||||
from stack_orchestrator import constants
|
from stack_orchestrator import constants
|
||||||
from stack_orchestrator.deploy.deployer_factory import getDeployer
|
from stack_orchestrator.deploy.deployer_factory import getDeployer
|
||||||
|
|
||||||
WEBAPP_PORT = 3000
|
WEBAPP_PORT = 80
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ def process_app_removal_request(ctx,
|
|||||||
matched_owner = match_owner(app_removal_request, deployment_record, dns_record)
|
matched_owner = match_owner(app_removal_request, deployment_record, dns_record)
|
||||||
|
|
||||||
# Or of the original deployment request.
|
# Or of the original deployment request.
|
||||||
if not matched_owner and deployment_record.request:
|
if not matched_owner and deployment_record.attributes.request:
|
||||||
matched_owner = match_owner(app_removal_request, laconic.get_record(deployment_record.request, require=True))
|
matched_owner = match_owner(app_removal_request, laconic.get_record(deployment_record.attributes.request, require=True))
|
||||||
|
|
||||||
if matched_owner:
|
if matched_owner:
|
||||||
print("Matched deployment ownership:", matched_owner)
|
print("Matched deployment ownership:", matched_owner)
|
||||||
|
|||||||
@@ -195,6 +195,23 @@ def file_hash(filename):
|
|||||||
return hashlib.sha1(open(filename).read().encode()).hexdigest()
|
return hashlib.sha1(open(filename).read().encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def determine_base_container(clone_dir, app_type="webapp"):
|
||||||
|
if not app_type or not app_type.startswith("webapp"):
|
||||||
|
raise Exception(f"Unsupported app_type {app_type}")
|
||||||
|
|
||||||
|
base_container = "cerc/webapp-base"
|
||||||
|
if app_type == "webapp/next":
|
||||||
|
base_container = "cerc/nextjs-base"
|
||||||
|
elif app_type == "webapp":
|
||||||
|
pkg_json_path = os.path.join(clone_dir, "package.json")
|
||||||
|
if os.path.exists(pkg_json_path):
|
||||||
|
pkg_json = json.load(open(pkg_json_path))
|
||||||
|
if "next" in pkg_json.get("dependencies", {}):
|
||||||
|
base_container = "cerc/nextjs-base"
|
||||||
|
|
||||||
|
return base_container
|
||||||
|
|
||||||
|
|
||||||
def build_container_image(app_record, tag, extra_build_args=[]):
|
def build_container_image(app_record, tag, extra_build_args=[]):
|
||||||
tmpdir = tempfile.mkdtemp()
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
|
||||||
@@ -216,8 +233,15 @@ def build_container_image(app_record, tag, extra_build_args=[]):
|
|||||||
result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir])
|
result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir])
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
|
|
||||||
|
base_container = determine_base_container(clone_dir, app_record.attributes.app_type)
|
||||||
|
|
||||||
print("Building webapp ...")
|
print("Building webapp ...")
|
||||||
build_command = [sys.argv[0], "build-webapp", "--source-repo", clone_dir, "--tag", tag]
|
build_command = [
|
||||||
|
sys.argv[0], "build-webapp",
|
||||||
|
"--source-repo", clone_dir,
|
||||||
|
"--tag", tag,
|
||||||
|
"--base-container", base_container
|
||||||
|
]
|
||||||
if extra_build_args:
|
if extra_build_args:
|
||||||
build_command.append("--extra-build-args")
|
build_command.append("--extra-build-args")
|
||||||
build_command.append(" ".join(extra_build_args))
|
build_command.append(" ".join(extra_build_args))
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import sys
|
|||||||
import ruamel.yaml
|
import ruamel.yaml
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dotenv import dotenv_values
|
from dotenv import dotenv_values
|
||||||
from typing import Mapping
|
from typing import Mapping, Set, List
|
||||||
|
|
||||||
|
|
||||||
def include_exclude_check(s, include, exclude):
|
def include_exclude_check(s, include, exclude):
|
||||||
@@ -81,17 +81,17 @@ def get_pod_list(parsed_stack):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_code_paths(stack):
|
def get_plugin_code_paths(stack) -> List[Path]:
|
||||||
parsed_stack = get_parsed_stack_config(stack)
|
parsed_stack = get_parsed_stack_config(stack)
|
||||||
pods = parsed_stack["pods"]
|
pods = parsed_stack["pods"]
|
||||||
result = []
|
result: Set[Path] = set()
|
||||||
for pod in pods:
|
for pod in pods:
|
||||||
if type(pod) is str:
|
if type(pod) is str:
|
||||||
result.append(get_stack_file_path(stack).parent)
|
result.add(get_stack_file_path(stack).parent)
|
||||||
else:
|
else:
|
||||||
pod_root_dir = os.path.join(get_dev_root_path(None), pod["repository"].split("/")[-1], pod["path"])
|
pod_root_dir = os.path.join(get_dev_root_path(None), pod["repository"].split("/")[-1], pod["path"])
|
||||||
result.append(Path(os.path.join(pod_root_dir, "stack")))
|
result.add(Path(os.path.join(pod_root_dir, "stack")))
|
||||||
return result
|
return list(result)
|
||||||
|
|
||||||
|
|
||||||
def get_pod_file_path(parsed_stack, pod_name: str):
|
def get_pod_file_path(parsed_stack, pod_name: str):
|
||||||
@@ -139,6 +139,13 @@ def get_compose_file_dir():
|
|||||||
return source_compose_dir
|
return source_compose_dir
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_file_dir():
|
||||||
|
# TODO: refactor to use common code with deploy command
|
||||||
|
data_dir = Path(__file__).absolute().parent.joinpath("data")
|
||||||
|
source_config_dir = data_dir.joinpath("config")
|
||||||
|
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:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fi
|
|||||||
|
|
||||||
# Helper functions: TODO move into a separate file
|
# Helper functions: TODO move into a separate file
|
||||||
wait_for_pods_started () {
|
wait_for_pods_started () {
|
||||||
for i in {1..5}
|
for i in {1..50}
|
||||||
do
|
do
|
||||||
local ps_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir ps )
|
local ps_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir ps )
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ wait_for_pods_started () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wait_for_log_output () {
|
wait_for_log_output () {
|
||||||
for i in {1..5}
|
for i in {1..50}
|
||||||
do
|
do
|
||||||
|
|
||||||
local log_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
local log_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
@@ -97,6 +97,10 @@ if [ ! "$create_file_content" == "create-command-output-data" ]; then
|
|||||||
echo "deploy create test: FAILED"
|
echo "deploy create test: FAILED"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 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 "deploy create output file test: passed"
|
echo "deploy create output file test: passed"
|
||||||
# Try to start the deployment
|
# Try to start the deployment
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
@@ -117,15 +121,26 @@ else
|
|||||||
echo "deployment config test: FAILED"
|
echo "deployment config test: FAILED"
|
||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check that the ConfigMap is mounted and contains the expected content.
|
||||||
|
log_output_4=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_4" == *"/config/test_config:"* ]] && [[ "$log_output_4" == *"dbfc7a4d-44a7-416d-b5f3-29842cc47650"* ]]; then
|
||||||
|
echo "deployment ConfigMap test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment ConfigMap test: FAILED"
|
||||||
|
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
|
||||||
sleep 2
|
# sleep for longer to check if that's why the subsequent create cluster fails
|
||||||
|
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_4=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
if [[ "$log_output_4" == *"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"
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ CHECK="SPECIAL_01234567890_TEST_STRING"
|
|||||||
|
|
||||||
set +e
|
set +e
|
||||||
|
|
||||||
CONTAINER_ID=$(docker run -p 3000:3000 -d -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG cerc/test-progressive-web-app:local)
|
CONTAINER_ID=$(docker run -p 3000:80 -d -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG cerc/test-progressive-web-app:local)
|
||||||
sleep 3
|
sleep 3
|
||||||
wget -t 7 -O test.before -m http://localhost:3000
|
wget -t 7 -O test.before -m http://localhost:3000
|
||||||
|
|
||||||
docker logs $CONTAINER_ID
|
docker logs $CONTAINER_ID
|
||||||
docker remove -f $CONTAINER_ID
|
docker remove -f $CONTAINER_ID
|
||||||
|
|
||||||
CONTAINER_ID=$(docker run -p 3000:3000 -e CERC_WEBAPP_DEBUG=$CHECK -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG -d cerc/test-progressive-web-app:local)
|
CONTAINER_ID=$(docker run -p 3000:80 -e CERC_WEBAPP_DEBUG=$CHECK -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG -d cerc/test-progressive-web-app:local)
|
||||||
sleep 3
|
sleep 3
|
||||||
wget -t 7 -O test.after -m http://localhost:3000
|
wget -t 7 -O test.after -m http://localhost:3000
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user