forked from cerc-io/stack-orchestrator
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e63ac91b3e | ||
|
|
faa1815aa8 | ||
|
|
3d3d617531 | ||
|
|
5c21fc1021 | ||
|
|
7dd98ee010 | ||
|
|
fe0b456b86 | ||
|
|
f094a74e66 | ||
|
|
d0d0602e88 | ||
|
|
777f4524de | ||
|
|
7c29b8c36c | ||
|
|
2659b87390 | ||
|
|
f636e6d844 | ||
|
|
d2b249dd79 | ||
|
|
b6f8015ab9 | ||
|
|
db4986dcc6 | ||
|
|
65f05ea80c | ||
|
|
01f9fe67ed | ||
|
|
049ffcff71 | ||
|
|
f5314a979b | ||
|
|
39f4fa4487 | ||
|
|
0b0394a940 | ||
|
|
37b9500483 | ||
|
|
3c3e582939 | ||
|
|
26d265360d | ||
|
|
f81b78cfbc | ||
|
|
d9bb6b3588 | ||
|
|
b59beb66eb | ||
|
|
65d67dba10 | ||
|
|
b22c72e715 | ||
|
|
c9444591f5 | ||
|
|
903f3b10e2 | ||
|
|
72ed2eb91a | ||
|
|
2104eb5f30 | ||
|
|
afd6be3b13 | ||
|
|
f914baa913 | ||
|
|
8be1e684e8 | ||
|
|
5d16251ce9 | ||
|
|
3309782439 | ||
|
|
4b3b3478e7 | ||
|
|
2a9955055c | ||
|
|
8964e1c0fe | ||
|
|
d2ebb81d77 | ||
|
|
4a981d8d2e | ||
|
|
88a0236ca9 | ||
|
|
937b983ec9 | ||
|
|
098567625a | ||
|
|
23ee3e19b7 | ||
|
|
2d764fc7d0 |
@@ -0,0 +1,21 @@
|
|||||||
|
name: Lint Checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: '*'
|
||||||
|
push:
|
||||||
|
branches: '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: "Run linter"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: "Clone project repository"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: "Install Python"
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name : "Run flake8"
|
||||||
|
uses: py-actions/flake8@v2
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
name: Container Registry Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: '*'
|
||||||
|
paths:
|
||||||
|
- '!**'
|
||||||
|
- '.gitea/workflows/triggers/test-container-registry'
|
||||||
|
- '.gitea/workflows/test-container-registry.yml'
|
||||||
|
- 'tests/container-registry/run-test.sh'
|
||||||
|
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||||
|
- cron: '6 19 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: "Run contaier registry hosting test on kind/k8s"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: "Clone project repository"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
# At present the stock setup-python action fails on Linux/aarch64
|
||||||
|
# Conditional steps below workaroud this by using deadsnakes for that case only
|
||||||
|
- name: "Install Python for ARM on Linux"
|
||||||
|
if: ${{ runner.arch == 'arm64' && runner.os == 'Linux' }}
|
||||||
|
uses: deadsnakes/action@v3.0.1
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Install Python cases other than ARM on Linux"
|
||||||
|
if: ${{ ! (runner.arch == 'arm64' && runner.os == 'Linux') }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Print Python version"
|
||||||
|
run: python3 --version
|
||||||
|
- name: "Install shiv"
|
||||||
|
run: pip install shiv
|
||||||
|
- name: "Generate build version file"
|
||||||
|
run: ./scripts/create_build_tag_file.sh
|
||||||
|
- name: "Build local shiv package"
|
||||||
|
run: ./scripts/build_shiv_package.sh
|
||||||
|
- name: "Check cgroups version"
|
||||||
|
run: mount | grep cgroup
|
||||||
|
- name: "Install kind"
|
||||||
|
run: ./tests/scripts/install-kind.sh
|
||||||
|
- name: "Install Kubectl"
|
||||||
|
run: ./tests/scripts/install-kubectl.sh
|
||||||
|
- name: "Install ed" # Only needed until we remove the need to edit the spec file
|
||||||
|
run: apt update && apt install -y ed
|
||||||
|
- name: "Run container registry deployment test"
|
||||||
|
run: |
|
||||||
|
source /opt/bash-utils/cgroup-helper.sh
|
||||||
|
join_cgroup
|
||||||
|
./tests/container-registry/run-test.sh
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
name: Database Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: '*'
|
||||||
|
paths:
|
||||||
|
- '!**'
|
||||||
|
- '.gitea/workflows/triggers/test-database'
|
||||||
|
- '.gitea/workflows/test-database.yml'
|
||||||
|
- 'tests/database/run-test.sh'
|
||||||
|
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||||
|
- cron: '5 18 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: "Run database hosting test on kind/k8s"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: "Clone project repository"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
# At present the stock setup-python action fails on Linux/aarch64
|
||||||
|
# Conditional steps below workaroud this by using deadsnakes for that case only
|
||||||
|
- name: "Install Python for ARM on Linux"
|
||||||
|
if: ${{ runner.arch == 'arm64' && runner.os == 'Linux' }}
|
||||||
|
uses: deadsnakes/action@v3.0.1
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Install Python cases other than ARM on Linux"
|
||||||
|
if: ${{ ! (runner.arch == 'arm64' && runner.os == 'Linux') }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
- name: "Print Python version"
|
||||||
|
run: python3 --version
|
||||||
|
- name: "Install shiv"
|
||||||
|
run: pip install shiv
|
||||||
|
- name: "Generate build version file"
|
||||||
|
run: ./scripts/create_build_tag_file.sh
|
||||||
|
- name: "Build local shiv package"
|
||||||
|
run: ./scripts/build_shiv_package.sh
|
||||||
|
- name: "Check cgroups version"
|
||||||
|
run: mount | grep cgroup
|
||||||
|
- name: "Install kind"
|
||||||
|
run: ./tests/scripts/install-kind.sh
|
||||||
|
- name: "Install Kubectl"
|
||||||
|
run: ./tests/scripts/install-kubectl.sh
|
||||||
|
- name: "Run database deployment test"
|
||||||
|
run: |
|
||||||
|
source /opt/bash-utils/cgroup-helper.sh
|
||||||
|
join_cgroup
|
||||||
|
./tests/database/run-test.sh
|
||||||
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
name: K8s Deploy Test
|
name: K8s Deploy Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: '*'
|
||||||
push:
|
push:
|
||||||
branches: '*'
|
branches: '*'
|
||||||
paths:
|
paths:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Change this file to trigger running the test-container-registry CI job
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Change this file to trigger running the test-database CI job
|
||||||
|
Trigger test run
|
||||||
@@ -29,10 +29,10 @@ chmod +x ~/.docker/cli-plugins/docker-compose
|
|||||||
Next decide on a directory where you would like to put the stack-orchestrator program. Typically this would be
|
Next decide on a directory where you would like to put the stack-orchestrator program. Typically this would be
|
||||||
a "user" binary directory such as `~/bin` or perhaps `/usr/local/laconic` or possibly just the current working directory.
|
a "user" binary directory such as `~/bin` or perhaps `/usr/local/laconic` or possibly just the current working directory.
|
||||||
|
|
||||||
Now, having selected that directory, download the latest release from [this page](https://github.com/cerc-io/stack-orchestrator/tags) into it (we're using `~/bin` below for concreteness but edit to suit if you selected a different directory). Also be sure that the destination directory exists and is writable:
|
Now, having selected that directory, download the latest release from [this page](https://git.vdb.to/cerc-io/stack-orchestrator/tags) into it (we're using `~/bin` below for concreteness but edit to suit if you selected a different directory). Also be sure that the destination directory exists and is writable:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -L -o ~/bin/laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so
|
curl -L -o ~/bin/laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so
|
||||||
```
|
```
|
||||||
|
|
||||||
Give it execute permissions:
|
Give it execute permissions:
|
||||||
@@ -52,7 +52,7 @@ Version: 1.1.0-7a607c2-202304260513
|
|||||||
Save the distribution url to `~/.laconic-so/config.yml`:
|
Save the distribution url to `~/.laconic-so/config.yml`:
|
||||||
```bash
|
```bash
|
||||||
mkdir ~/.laconic-so
|
mkdir ~/.laconic-so
|
||||||
echo "distribution-url: https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so" > ~/.laconic-so/config.yml
|
echo "distribution-url: https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so" > ~/.laconic-so/config.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update
|
### Update
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ In addition to the pre-requisites listed in the [README](/README.md), the follow
|
|||||||
|
|
||||||
1. Clone this repository:
|
1. Clone this repository:
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/cerc-io/stack-orchestrator.git
|
$ git clone https://git.vdb.to/cerc-io/stack-orchestrator.git
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Enter the project directory:
|
2. Enter the project directory:
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Adding a new stack
|
# Adding a new stack
|
||||||
|
|
||||||
See [this PR](https://github.com/cerc-io/stack-orchestrator/pull/434) for an example of how to currently add a minimal stack to stack orchestrator. The [reth stack](https://github.com/cerc-io/stack-orchestrator/pull/435) is another good example.
|
See [this PR](https://git.vdb.to/cerc-io/stack-orchestrator/pull/434) for an example of how to currently add a minimal stack to stack orchestrator. The [reth stack](https://git.vdb.to/cerc-io/stack-orchestrator/pull/435) is another good example.
|
||||||
|
|
||||||
For external developers, we recommend forking this repo and adding your stack directly to your fork. This initially requires running in "developer mode" as described [here](/docs/CONTRIBUTING.md). Check out the [Namada stack](https://github.com/vknowable/stack-orchestrator/blob/main/app/data/stacks/public-namada/digitalocean_quickstart.md) from Knowable to see how that is done.
|
For external developers, we recommend forking this repo and adding your stack directly to your fork. This initially requires running in "developer mode" as described [here](/docs/CONTRIBUTING.md). Check out the [Namada stack](https://github.com/vknowable/stack-orchestrator/blob/main/app/data/stacks/public-namada/digitalocean_quickstart.md) from Knowable to see how that is done.
|
||||||
|
|
||||||
Core to the feature completeness of stack orchestrator is to [decouple the tool functionality from payload](https://github.com/cerc-io/stack-orchestrator/issues/315) which will no longer require forking to add a stack.
|
Core to the feature completeness of stack orchestrator is to [decouple the tool functionality from payload](https://git.vdb.to/cerc-io/stack-orchestrator/issues/315) which will no longer require forking to add a stack.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# Specification
|
# Specification
|
||||||
|
|
||||||
Note: this page is out of date (but still useful) - it will no longer be useful once stacks are [decoupled from the tool functionality](https://github.com/cerc-io/stack-orchestrator/issues/315).
|
Note: this page is out of date (but still useful) - it will no longer be useful once stacks are [decoupled from the tool functionality](https://git.vdb.to/cerc-io/stack-orchestrator/issues/315).
|
||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ pydantic==1.10.9
|
|||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
validators==0.22.0
|
validators==0.22.0
|
||||||
kubernetes>=28.1.0
|
kubernetes>=28.1.0
|
||||||
|
humanfriendly>=10.0
|
||||||
|
|||||||
@@ -41,4 +41,4 @@ runcmd:
|
|||||||
- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
- systemctl enable docker
|
- systemctl enable docker
|
||||||
- systemctl start docker
|
- systemctl start docker
|
||||||
- git clone https://github.com/cerc-io/stack-orchestrator.git /home/ubuntu/stack-orchestrator
|
- git clone https://git.vdb.to/cerc-io/stack-orchestrator.git /home/ubuntu/stack-orchestrator
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ runcmd:
|
|||||||
- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
- systemctl enable docker
|
- systemctl enable docker
|
||||||
- systemctl start docker
|
- systemctl start docker
|
||||||
- curl -L -o /usr/local/bin/laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so
|
- curl -L -o /usr/local/bin/laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so
|
||||||
- chmod +x /usr/local/bin/laconic-so
|
- chmod +x /usr/local/bin/laconic-so
|
||||||
|
|||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Beginnings of a script to quickly spin up and test a deployment
|
||||||
|
if [[ -n "$CERC_SCRIPT_DEBUG" ]]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
stack_name=$1
|
||||||
|
else
|
||||||
|
stack_name="test"
|
||||||
|
fi
|
||||||
|
spec_file_name="${stack_name}-spec.yml"
|
||||||
|
deployment_dir_name="${stack_name}-deployment"
|
||||||
|
rm -f ${spec_file_name}
|
||||||
|
rm -rf ${deployment_dir_name}
|
||||||
|
laconic-so --stack ${stack_name} deploy --deploy-to compose init --output ${spec_file_name}
|
||||||
|
laconic-so --stack ${stack_name} deploy --deploy-to compose create --deployment-dir ${deployment_dir_name} --spec-file ${spec_file_name}
|
||||||
|
#laconic-so deployment --dir ${deployment_dir_name} start
|
||||||
|
#laconic-so deployment --dir ${deployment_dir_name} ps
|
||||||
|
#laconic-so deployment --dir ${deployment_dir_name} stop
|
||||||
@@ -137,7 +137,7 @@ fi
|
|||||||
echo "**************************************************************************************"
|
echo "**************************************************************************************"
|
||||||
echo "Installing laconic-so"
|
echo "Installing laconic-so"
|
||||||
# install latest `laconic-so`
|
# install latest `laconic-so`
|
||||||
distribution_url=https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so
|
distribution_url=https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so
|
||||||
install_filename=${install_dir}/laconic-so
|
install_filename=${install_dir}/laconic-so
|
||||||
mkdir -p ${install_dir}
|
mkdir -p ${install_dir}
|
||||||
curl -L -o ${install_filename} ${distribution_url}
|
curl -L -o ${install_filename} ${distribution_url}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ setup(
|
|||||||
description='Orchestrates deployment of the Laconic stack',
|
description='Orchestrates deployment of the Laconic stack',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url='https://github.com/cerc-io/stack-orchestrator',
|
url='https://git.vdb.to/cerc-io/stack-orchestrator',
|
||||||
py_modules=['stack_orchestrator'],
|
py_modules=['stack_orchestrator'],
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[requirements],
|
install_requires=[requirements],
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import subprocess
|
|||||||
import click
|
import click
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from stack_orchestrator.util import include_exclude_check, get_parsed_stack_config, stack_is_external
|
from stack_orchestrator.util import include_exclude_check, get_parsed_stack_config, stack_is_external, warn_exit
|
||||||
from stack_orchestrator.base import get_npm_registry_url
|
from stack_orchestrator.base import get_npm_registry_url
|
||||||
|
|
||||||
# TODO: find a place for this
|
# TODO: find a place for this
|
||||||
@@ -164,6 +164,8 @@ def command(ctx, include, exclude, force_rebuild, extra_build_args):
|
|||||||
containers_in_scope = []
|
containers_in_scope = []
|
||||||
if stack:
|
if stack:
|
||||||
stack_config = get_parsed_stack_config(stack)
|
stack_config = get_parsed_stack_config(stack)
|
||||||
|
if "containers" not in stack_config or stack_config["containers"] is None:
|
||||||
|
warn_exit(f"stack {stack} does not define any containers")
|
||||||
containers_in_scope = stack_config['containers']
|
containers_in_scope = stack_config['containers']
|
||||||
else:
|
else:
|
||||||
containers_in_scope = all_containers
|
containers_in_scope = all_containers
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ kube_config_key = "kube-config"
|
|||||||
deploy_to_key = "deploy-to"
|
deploy_to_key = "deploy-to"
|
||||||
network_key = "network"
|
network_key = "network"
|
||||||
http_proxy_key = "http-proxy"
|
http_proxy_key = "http-proxy"
|
||||||
image_resigtry_key = "image-registry"
|
image_registry_key = "image-registry"
|
||||||
|
configmaps_key = "configmaps"
|
||||||
|
resources_key = "resources"
|
||||||
|
volumes_key = "volumes"
|
||||||
|
security_key = "security"
|
||||||
|
annotations_key = "annotations"
|
||||||
|
labels_key = "labels"
|
||||||
kind_config_filename = "kind-config.yml"
|
kind_config_filename = "kind-config.yml"
|
||||||
kube_config_filename = "kubeconfig.yml"
|
kube_config_filename = "kubeconfig.yml"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
registry:
|
||||||
|
image: registry:2.8
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
REGISTRY_LOG_LEVEL: ${REGISTRY_LOG_LEVEL}
|
||||||
|
volumes:
|
||||||
|
- registry-data:/var/lib/registry
|
||||||
|
ports:
|
||||||
|
- "5000"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
registry-data:
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
version: "3.2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mars:
|
||||||
|
image: cerc/mars-v2:local
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- URL_OSMOSIS_REST=https://lcd-osmosis.blockapsis.com
|
||||||
|
- URL_OSMOSIS_RPC=https://rpc-osmosis.blockapsis.com
|
||||||
|
- WALLET_CONNECT_ID=0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
version: "3.2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mars:
|
||||||
|
image: cerc/mars:local
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- URL_OSMOSIS_GQL=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-hive-front/graphql
|
||||||
|
- URL_OSMOSIS_REST=https://lcd-osmosis.blockapsis.com
|
||||||
|
- URL_OSMOSIS_RPC=https://rpc-osmosis.blockapsis.com
|
||||||
|
- URL_NEUTRON_GQL=https://neutron.rpc.p2p.world/qgrnU6PsQZA8F9S5Fb8Fn3tV3kXmMBl2M9bcc9jWLjQy8p/hive/graphql
|
||||||
|
- URL_NEUTRON_REST=https://rest-kralum.neutron-1.neutron.org
|
||||||
|
- URL_NEUTRON_RPC=https://rpc-kralum.neutron-1.neutron.org
|
||||||
|
- URL_NEUTRON_TEST_GQL=https://testnet-neutron-gql.marsprotocol.io/graphql
|
||||||
|
- URL_NEUTRON_TEST_REST=https://rest-palvus.pion-1.ntrn.tech
|
||||||
|
- URL_NEUTRON_TEST_RPC=https://rpc-palvus.pion-1.ntrn.tech
|
||||||
|
- WALLET_CONNECT_ID=0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
version: '3.2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Builds and serves the MobyMask v3 react-app
|
||||||
|
mobymask-v3-app:
|
||||||
|
restart: unless-stopped
|
||||||
|
image: cerc/mobymask-ui:local
|
||||||
|
environment:
|
||||||
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
|
CERC_APP_WATCHER_URL: ${CERC_APP_WATCHER_URL}
|
||||||
|
CERC_SNAP_URL: ${CERC_SNAP_URL}
|
||||||
|
working_dir: /app
|
||||||
|
command: ["bash", "/scripts/mobymask-app-start.sh"]
|
||||||
|
volumes:
|
||||||
|
- ../config/watcher-mobymask-v3-demo/mobymask-app-start.sh:/scripts/mobymask-app-start.sh
|
||||||
|
- ../config/watcher-mobymask-v3-demo/local/keys:/server/keys
|
||||||
|
- mobymask_deployment:/server
|
||||||
|
- nitro_deployment:/nitro
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3004:80"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "nc", "-vz", "localhost", "80"]
|
||||||
|
interval: 20s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 15
|
||||||
|
start_period: 10s
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mobymask_deployment:
|
||||||
|
nitro_deployment:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
version: "3.2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
ping-pub:
|
||||||
|
image: cerc/ping-pub:local
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
snowballtools-base-backend:
|
||||||
|
image: cerc/snowballtools-base-backend:local
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- data:/data
|
||||||
|
- config:/config:ro
|
||||||
|
ports:
|
||||||
|
- 8000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
config:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: cerc/test-database-container:local
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- db-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: "test-user"
|
||||||
|
POSTGRES_DB: "test-db"
|
||||||
|
POSTGRES_PASSWORD: "password"
|
||||||
|
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
|
||||||
|
ports:
|
||||||
|
- "5432"
|
||||||
|
|
||||||
|
test-client:
|
||||||
|
image: cerc/test-database-client:local
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-data:
|
||||||
@@ -5,12 +5,15 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED}
|
CERC_TEST_PARAM_1: ${CERC_TEST_PARAM_1:-FAILED}
|
||||||
|
CERC_TEST_PARAM_2: "CERC_TEST_PARAM_2_VALUE"
|
||||||
volumes:
|
volumes:
|
||||||
- test-data:/data
|
- test-data-bind:/data
|
||||||
|
- test-data-auto:/data2
|
||||||
- test-config:/config:ro
|
- test-config:/config:ro
|
||||||
ports:
|
ports:
|
||||||
- "80"
|
- "80"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
test-data:
|
test-data-bind:
|
||||||
|
test-data-auto:
|
||||||
test-config:
|
test-config:
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
version: '3.2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Starts the PostgreSQL database for watcher
|
||||||
|
mobymask-watcher-db:
|
||||||
|
restart: unless-stopped
|
||||||
|
image: postgres:14-alpine
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=vdbm
|
||||||
|
- POSTGRES_MULTIPLE_DATABASES=mobymask-watcher-1,mobymask-watcher-1-job-queue,mobymask-watcher-2,mobymask-watcher-2-job-queue,mobymask-watcher-3,mobymask-watcher-3-job-queue
|
||||||
|
- POSTGRES_EXTENSION=mobymask-watcher-1-job-queue:pgcrypto,mobymask-watcher-2-job-queue:pgcrypto,mobymask-watcher-3-job-queue:pgcrypto
|
||||||
|
- POSTGRES_PASSWORD=password
|
||||||
|
volumes:
|
||||||
|
- ../config/postgresql/multiple-postgressql-databases.sh:/docker-entrypoint-initdb.d/multiple-postgressql-databases.sh
|
||||||
|
- mobymask_watcher_db_data:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "nc", "-v", "localhost", "5432"]
|
||||||
|
interval: 20s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 15
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
# Deploys the MobyMask contract and generates an invite link
|
||||||
|
# Deployment is skipped if CERC_DEPLOYED_CONTRACT env is set
|
||||||
|
mobymask:
|
||||||
|
image: cerc/mobymask:local
|
||||||
|
working_dir: /app/packages/server
|
||||||
|
environment:
|
||||||
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
|
# ENV: "PROD" # Check if required
|
||||||
|
CERC_ETH_RPC_ENDPOINT: ${CERC_ETH_RPC_ENDPOINT}
|
||||||
|
CERC_PRIVATE_KEY_DEPLOYER: ${CERC_PRIVATE_KEY_DEPLOYER}
|
||||||
|
CERC_MOBYMASK_APP_BASE_URI: ${CERC_MOBYMASK_APP_BASE_URI}
|
||||||
|
CERC_DEPLOYED_CONTRACT: ${CERC_DEPLOYED_CONTRACT}
|
||||||
|
command: ["bash", "-c", "./deploy-and-generate-invite.sh"]
|
||||||
|
volumes:
|
||||||
|
- ../config/watcher-mobymask-v3-demo/deploy-and-generate-invite.sh:/app/packages/server/deploy-and-generate-invite.sh
|
||||||
|
- mobymask_deployment:/app/packages/server
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
# Starts a MobyMask v3 watcher server #1
|
||||||
|
mobymask-watcher-1-server:
|
||||||
|
image: cerc/watcher-mobymask-v3:local
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
mobymask-watcher-db:
|
||||||
|
condition: service_healthy
|
||||||
|
mobymask:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
nitro-contracts:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
environment:
|
||||||
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
|
CERC_WATCHER_INDEX: 1
|
||||||
|
CERC_ETH_RPC_QUERY_ENDPOINT: ${CERC_ETH_RPC_QUERY_ENDPOINT}
|
||||||
|
CERC_ETH_RPC_MUTATION_ENDPOINT: ${CERC_ETH_RPC_MUTATION_ENDPOINT}
|
||||||
|
CERC_NITRO_CHAIN_URL: ${CERC_NITRO_CHAIN_URL}
|
||||||
|
command: ["bash", "./start-server.sh"]
|
||||||
|
volumes:
|
||||||
|
- ../config/watcher-mobymask-v3-demo/watcher-config-template.toml:/app/environments/watcher-config-template.toml
|
||||||
|
- ../config/watcher-mobymask-v3-demo/watcher-config-rates.toml:/app/environments/rates.toml
|
||||||
|
- ../config/watcher-mobymask-v3-demo/local/keys:/app/keys
|
||||||
|
- ../config/watcher-mobymask-v3-demo/start-server.sh:/app/start-server.sh
|
||||||
|
- watcher_nitro_data:/app/out/nitro-db
|
||||||
|
- nitro_deployment:/nitro
|
||||||
|
- mobymask_deployment:/server
|
||||||
|
# Expose GQL, metrics and relay node ports
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:3001:3001"
|
||||||
|
- "9001"
|
||||||
|
- "0.0.0.0:9090:9090"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "busybox", "nc", "localhost", "9090"]
|
||||||
|
interval: 20s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 15
|
||||||
|
start_period: 5s
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
# Starts a MobyMask v3 watcher server #2
|
||||||
|
mobymask-watcher-2-server:
|
||||||
|
image: cerc/watcher-mobymask-v3:local
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
mobymask-watcher-1-server:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
|
CERC_WATCHER_INDEX: 2
|
||||||
|
CERC_ETH_RPC_QUERY_ENDPOINT: ${CERC_ETH_RPC_QUERY_ENDPOINT}
|
||||||
|
CERC_ETH_RPC_MUTATION_ENDPOINT: ${CERC_ETH_RPC_MUTATION_ENDPOINT}
|
||||||
|
CERC_NITRO_CHAIN_URL: ${CERC_NITRO_CHAIN_URL}
|
||||||
|
command: ["bash", "./start-server.sh"]
|
||||||
|
volumes:
|
||||||
|
- ../config/watcher-mobymask-v3-demo/watcher-config-template.toml:/app/environments/watcher-config-template.toml
|
||||||
|
- ../config/watcher-mobymask-v3-demo/watcher-config-rates.toml:/app/environments/rates.toml
|
||||||
|
- ../config/watcher-mobymask-v3-demo/local/keys:/app/keys
|
||||||
|
- ../config/watcher-mobymask-v3-demo/start-server.sh:/app/start-server.sh
|
||||||
|
- watcher_nitro_data:/app/out/nitro-db
|
||||||
|
- nitro_deployment:/nitro
|
||||||
|
- mobymask_deployment:/server
|
||||||
|
# Expose GQL, metrics and relay node ports
|
||||||
|
ports:
|
||||||
|
- "3001"
|
||||||
|
- "9001"
|
||||||
|
- "0.0.0.0:9091:9090"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "busybox", "nc", "localhost", "9090"]
|
||||||
|
interval: 20s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 15
|
||||||
|
start_period: 5s
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
# Starts a MobyMask v3 watcher server #3
|
||||||
|
mobymask-watcher-3-server:
|
||||||
|
image: cerc/watcher-mobymask-v3:local
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
mobymask-watcher-1-server:
|
||||||
|
condition: service_healthy
|
||||||
|
mobymask-watcher-2-server:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
|
CERC_WATCHER_INDEX: 3
|
||||||
|
CERC_ETH_RPC_QUERY_ENDPOINT: ${CERC_ETH_RPC_QUERY_ENDPOINT}
|
||||||
|
CERC_ETH_RPC_MUTATION_ENDPOINT: ${CERC_ETH_RPC_MUTATION_ENDPOINT}
|
||||||
|
CERC_NITRO_CHAIN_URL: ${CERC_NITRO_CHAIN_URL}
|
||||||
|
command: ["bash", "./start-server.sh"]
|
||||||
|
volumes:
|
||||||
|
- ../config/watcher-mobymask-v3-demo/watcher-config-template.toml:/app/environments/watcher-config-template.toml
|
||||||
|
- ../config/watcher-mobymask-v3-demo/watcher-config-rates.toml:/app/environments/rates.toml
|
||||||
|
- ../config/watcher-mobymask-v3-demo/local/keys:/app/keys
|
||||||
|
- ../config/watcher-mobymask-v3-demo/start-server.sh:/app/start-server.sh
|
||||||
|
- watcher_nitro_data:/app/out/nitro-db
|
||||||
|
- nitro_deployment:/nitro
|
||||||
|
- mobymask_deployment:/server
|
||||||
|
# Expose GQL, metrics and relay node ports
|
||||||
|
ports:
|
||||||
|
- "3001"
|
||||||
|
- "9001"
|
||||||
|
- "0.0.0.0:9092:9090"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "busybox", "nc", "localhost", "9090"]
|
||||||
|
interval: 20s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 15
|
||||||
|
start_period: 5s
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mobymask_watcher_db_data:
|
||||||
|
mobymask_deployment:
|
||||||
|
nitro_deployment:
|
||||||
|
watcher_nitro_data:
|
||||||
@@ -5,4 +5,4 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG}
|
||||||
ports:
|
ports:
|
||||||
- "3000"
|
- "80"
|
||||||
|
|||||||
+1
-1
@@ -169,4 +169,4 @@ cp -a /app/packages/contracts-bedrock/deployments/$DEPLOYMENT_CONTEXT /l1-deploy
|
|||||||
cp /app/packages/contracts-bedrock/deploy-config/$DEPLOYMENT_CONTEXT.json /l2-config
|
cp /app/packages/contracts-bedrock/deploy-config/$DEPLOYMENT_CONTEXT.json /l2-config
|
||||||
openssl rand -hex 32 > /l2-config/l2-jwt.txt
|
openssl rand -hex 32 > /l2-config/l2-jwt.txt
|
||||||
|
|
||||||
echo "Deployment successful. Exiting"
|
echo "Deployment successful. Exiting."
|
||||||
|
|||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
# From env:
|
||||||
|
# CERC_ETH_RPC_ENDPOINT
|
||||||
|
# CERC_MOBYMASK_APP_BASE_URI
|
||||||
|
# CERC_DEPLOYED_CONTRACT
|
||||||
|
# CERC_PRIVATE_KEY_DEPLOYER
|
||||||
|
|
||||||
|
# Check if CERC_DEPLOYED_CONTRACT environment variable set to skip contract deployment
|
||||||
|
if [ -n "$CERC_DEPLOYED_CONTRACT" ]; then
|
||||||
|
echo "CERC_DEPLOYED_CONTRACT is set to '$CERC_DEPLOYED_CONTRACT'"
|
||||||
|
echo "Skipping contract deployment"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Using ETH RPC endpoint ${CERC_ETH_RPC_ENDPOINT}"
|
||||||
|
|
||||||
|
# Wait till ETH RPC endpoint is available with block number > 1
|
||||||
|
retry_interval=5
|
||||||
|
while true; do
|
||||||
|
block_number_hex=$(curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ${CERC_ETH_RPC_ENDPOINT} | jq -r '.result')
|
||||||
|
|
||||||
|
# Check if the request call was successful
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "RPC endpoint not yet available, retrying in $retry_interval seconds..."
|
||||||
|
sleep $retry_interval
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Convert hex to decimal
|
||||||
|
block_number_dec=$(printf %u ${block_number_hex})
|
||||||
|
|
||||||
|
# Check if block number is > 1 to avoid failures in the deployment
|
||||||
|
if [ "$block_number_dec" -ge 1 ]; then
|
||||||
|
echo "RPC endpoint is up"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "RPC endpoint not yet available, retrying in $retry_interval seconds..."
|
||||||
|
sleep $retry_interval
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Using CERC_PRIVATE_KEY_DEPLOYER from env"
|
||||||
|
|
||||||
|
# Create the required JSON and write it to a file
|
||||||
|
secrets_file="secrets.json"
|
||||||
|
secrets_json=$(jq -n \
|
||||||
|
--arg privateKey "$CERC_PRIVATE_KEY_DEPLOYER" \
|
||||||
|
--arg rpcUrl "$CERC_ETH_RPC_ENDPOINT" \
|
||||||
|
--arg baseURI "$CERC_MOBYMASK_APP_BASE_URI" \
|
||||||
|
'.privateKey = $privateKey | .rpcUrl = $rpcUrl | .baseURI = $baseURI')
|
||||||
|
echo "$secrets_json" > "${secrets_file}"
|
||||||
|
|
||||||
|
export RPC_URL="${CERC_ETH_RPC_ENDPOINT}"
|
||||||
|
|
||||||
|
# Check and exit if a deployment already exists (on restarts)
|
||||||
|
if [ -f ./config.json ]; then
|
||||||
|
echo "config.json already exists, checking the contract deployment"
|
||||||
|
|
||||||
|
# Read JSON file
|
||||||
|
deployment_details=$(cat config.json)
|
||||||
|
deployed_contract=$(echo "$deployment_details" | jq -r '.address')
|
||||||
|
|
||||||
|
cd ../hardhat
|
||||||
|
if yarn verifyDeployment --network optimism --contract "${deployed_contract}"; then
|
||||||
|
echo "Deployment verfication successful"
|
||||||
|
cd ../server
|
||||||
|
else
|
||||||
|
echo "Deployment verfication failed, please clear MobyMask deployment volume before starting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm run deployAndGenerateInvite
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Watchers
|
||||||
|
|
||||||
|
CERC_ETH_RPC_ENDPOINT="http://op-geth:8545"
|
||||||
|
|
||||||
|
# L1 pre-funded account #4, funded on L2 as well
|
||||||
|
CERC_PRIVATE_KEY_DEPLOYER="0xbe4aa664815ea3bc3d63118649a733f6c96b243744310806ecb6d96359ab62cf"
|
||||||
|
CERC_MOBYMASK_APP_BASE_URI="http://127.0.0.1:3004/#"
|
||||||
|
|
||||||
|
CERC_ETH_RPC_QUERY_ENDPOINT="http://op-geth:8545"
|
||||||
|
CERC_ETH_RPC_MUTATION_ENDPOINT="http://op-geth:8545"
|
||||||
|
CERC_NITRO_CHAIN_URL="http://op-geth:8546"
|
||||||
|
|
||||||
|
# App
|
||||||
|
|
||||||
|
CERC_APP_WATCHER_URL="http://localhost:3001"
|
||||||
|
CERC_SNAP_URL=http://localhost:8080
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "*************************************"
|
||||||
|
|
||||||
|
CERC_L1_RPC="${CERC_L1_RPC:-${DEFAULT_CERC_L1_RPC}}"
|
||||||
|
CERC_L1_CHAIN_ID="${CERC_L1_CHAIN_ID:-${DEFAULT_CERC_L1_CHAIN_ID}}"
|
||||||
|
CERC_L1_ACCOUNTS_CSV_URL="${CERC_L1_ACCOUNTS_CSV_URL:-${DEFAULT_CERC_L1_ACCOUNTS_CSV_URL}}"
|
||||||
|
|
||||||
|
# Fixture accounts
|
||||||
|
WATCHER_1_ACCOUNT=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
|
||||||
|
WATCHER_2_ACCOUNT=0x70997970C51812dc3A010C7d01b50e0d17dc79C8
|
||||||
|
WATCHER_3_ACCOUNT=0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65
|
||||||
|
NITRO_APP_ACCOUNT=0xbDA5747bFD65F08deb54cb465eB87D40e51B197E
|
||||||
|
|
||||||
|
WATCHER_1_ACCOUNT_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
|
||||||
|
WATCHER_2_ACCOUNT_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
|
||||||
|
WATCHER_3_ACCOUNT_KEY=0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
|
||||||
|
NITRO_APP_ACCOUNT_KEY=0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd
|
||||||
|
|
||||||
|
# Check if watcher 1 account already funded
|
||||||
|
WATCHER_1_ACCOUNT_BALANCE=$(cast balance ${WATCHER_1_ACCOUNT} --rpc-url $CERC_L1_RPC)
|
||||||
|
echo "WATCHER_1_ACCOUNT_BALANCE ${WATCHER_1_ACCOUNT_BALANCE}"
|
||||||
|
if [ "$WATCHER_1_ACCOUNT_BALANCE" != "0" ]; then
|
||||||
|
echo "Watcher account already funded, exiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Funding accounts on L2"
|
||||||
|
|
||||||
|
# Fetch the L1 funded accounts
|
||||||
|
curl -o accounts.csv $CERC_L1_ACCOUNTS_CSV_URL
|
||||||
|
|
||||||
|
# Use the accounts other than the ones used for Optimism deployment
|
||||||
|
ACCOUNT_1=$(awk -F ',' 'NR == 5 {print $2}' accounts.csv)
|
||||||
|
ACCOUNT_1_KEY=$(awk -F ',' 'NR == 5 {print $3}' accounts.csv)
|
||||||
|
|
||||||
|
ACCOUNT_2=$(awk -F ',' 'NR == 6 {print $2}' accounts.csv)
|
||||||
|
ACCOUNT_2_KEY=$(awk -F ',' 'NR == 6 {print $3}' accounts.csv)
|
||||||
|
|
||||||
|
# Get the bridge contract address
|
||||||
|
DEPLOYMENT_CONTEXT="$CERC_L1_CHAIN_ID"
|
||||||
|
BRIDGE=$(cat /l1-deployment/$DEPLOYMENT_CONTEXT/L1StandardBridgeProxy.json | jq -r .address)
|
||||||
|
|
||||||
|
# Send balance to bridge contract on L1
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $ACCOUNT_1 --value 10000ether $BRIDGE --private-key $ACCOUNT_1_KEY
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $ACCOUNT_2 --value 10000ether $BRIDGE --private-key $ACCOUNT_2_KEY
|
||||||
|
|
||||||
|
echo "Following accounts have been funded; use them for transactions on L2:"
|
||||||
|
echo "${ACCOUNT_1}"
|
||||||
|
echo "${ACCOUNT_2}"
|
||||||
|
|
||||||
|
echo "*************************************"
|
||||||
|
echo "Funding the watcher and app Nitro accounts on L2"
|
||||||
|
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $ACCOUNT_1 --value 200ether $WATCHER_1_ACCOUNT --private-key $ACCOUNT_1_KEY
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $ACCOUNT_1 --value 200ether $WATCHER_2_ACCOUNT --private-key $ACCOUNT_1_KEY
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $ACCOUNT_1 --value 200ether $WATCHER_3_ACCOUNT --private-key $ACCOUNT_1_KEY
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $ACCOUNT_1 --value 200ether $NITRO_APP_ACCOUNT --private-key $ACCOUNT_1_KEY
|
||||||
|
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $WATCHER_1_ACCOUNT --value 100ether $BRIDGE --private-key $WATCHER_1_ACCOUNT_KEY
|
||||||
|
sleep 5
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $WATCHER_2_ACCOUNT --value 100ether $BRIDGE --private-key $WATCHER_2_ACCOUNT_KEY
|
||||||
|
sleep 5
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $WATCHER_3_ACCOUNT --value 100ether $BRIDGE --private-key $WATCHER_3_ACCOUNT_KEY
|
||||||
|
sleep 5
|
||||||
|
cast send --rpc-url $CERC_L1_RPC --from $NITRO_APP_ACCOUNT --value 100ether $BRIDGE --private-key $NITRO_APP_ACCOUNT_KEY
|
||||||
|
|
||||||
|
echo "Done, exiting."
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"peerId": {
|
||||||
|
"id": "12D3KooWAMjBkFCT9DtCnSDcxftxJzSuTBvzVojabv64cnEvX4AZ",
|
||||||
|
"privKey": "CAESQAKCrnY0QKTky1I18fqn+VPydXGUv1NYiV+nVKqBFkw/CAjE9sKKIDGnYAo8mivnI6dngFenERY+0Q8AJrPTaXY=",
|
||||||
|
"pubKey": "CAESIAgIxPbCiiAxp2AKPJor5yOnZ4BXpxEWPtEPACaz02l2"
|
||||||
|
},
|
||||||
|
"relayPeerId": {
|
||||||
|
"id": "12D3KooWNQJDGtH3YAZk2qG1ZbcoQvw6HaD814pfd9HHQthgX63K",
|
||||||
|
"privKey": "CAESQGc996T2OuyV3aY+hnna+GIexKBEcE6jDNX/RaofHr58uv2EYxXm4kcL2tO2tKgnPgtkegHFdjqy6h/xOA+YMZo=",
|
||||||
|
"pubKey": "CAESILr9hGMV5uJHC9rTtrSoJz4LZHoBxXY6suof8TgPmDGa"
|
||||||
|
},
|
||||||
|
"peer": {
|
||||||
|
"privateKey": "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
|
||||||
|
"nitroAddress": "0xAAA6628Ec44A8a742987EF3A114dDFE2D4F7aDCE",
|
||||||
|
"nitroPrivateKey": "2d999770f7b5d49b694080f987b82bbc9fc9ac2b4dcc10b0f8aba7d700f69c6d"
|
||||||
|
},
|
||||||
|
"consensus": {
|
||||||
|
"publicKey": "02cd17b05ca998955be5ca7bf4fd4531243d438f1aae7ce8a0ed5159f53cee5b40",
|
||||||
|
"privateKey": "67d80505614bdf61fca11cbad31d93acb2c7df1c653dc25975d77d05f05f154f"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"peerId": {
|
||||||
|
"id": "12D3KooWBNEbY3QS4y23ngupDw9PDc4bvNvRJGVRejjV9EZLjux5",
|
||||||
|
"privKey": "CAESQGSTw0ymvn8+wX9Dbvyr4/Gib1q2voe0CC0VyeClMQP6FwW14x0fpRbBIx0XhLdxWHkRndphVg3gVAHyC+7ZI8o=",
|
||||||
|
"pubKey": "CAESIBcFteMdH6UWwSMdF4S3cVh5EZ3aYVYN4FQB8gvu2SPK"
|
||||||
|
},
|
||||||
|
"relayPeerId": {
|
||||||
|
"id": "12D3KooWPpBt2coC5BKEsewogHSkdgdyY9guUHJBgJFZoh66R4UL",
|
||||||
|
"privKey": "CAESQBdoGZzpiHsE8GlIhGGP+tl6A13sIPxOhgSwsWZR4cwmz/f7627X5GQRLD41ikH6oZ7ZeSbiiPsBtelL1+t3M80=",
|
||||||
|
"pubKey": "CAESIM/3++tu1+RkESw+NYpB+qGe2Xkm4oj7AbXpS9frdzPN"
|
||||||
|
},
|
||||||
|
"peer": {
|
||||||
|
"privateKey": "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
|
||||||
|
"nitroAddress": "0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94",
|
||||||
|
"nitroPrivateKey": "0279651921cd800ac560c21ceea27aab0107b67daf436cdd25ce84cad30159b4"
|
||||||
|
},
|
||||||
|
"consensus": {
|
||||||
|
"publicKey": "029c8035b3e9401b8f178f7c37285b5cb22501e017340e2058b3b842f2a1f0ae45",
|
||||||
|
"privateKey": "0261008e8e3ec808168e99333599da38ca59a056a2ae4510a6ad3d8b5cb0918c"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"peerId": {
|
||||||
|
"id": "12D3KooWSRH6ftgkAZsKZK7UX1Zr6Hx6YAsEepHqzopFszqfTxxi",
|
||||||
|
"privKey": "CAESQHBjlHxfVhZ2gXsBItrIEEgSGKcjMkFiGs3PPz9E3ace9qyWEkvR4oit5ve9SAROVoh20hoa42IC91NIafMaqws=",
|
||||||
|
"pubKey": "CAESIPaslhJL0eKIreb3vUgETlaIdtIaGuNiAvdTSGnzGqsL"
|
||||||
|
},
|
||||||
|
"relayPeerId": {
|
||||||
|
"id": "12D3KooWLudDC3NnS8k9xLiqTZcS9xbFNrGSttu5GPynQgNgenPW",
|
||||||
|
"privKey": "CAESQHuEXPB1hTQATlZaNRQnAfSvnfYUeLF9PjSNKp9L33K2pMl37tTBYghnfQ0yluse0zHOIXRJM+9+ARn9+jIr0gU=",
|
||||||
|
"pubKey": "CAESIKTJd+7UwWIIZ30NMpbrHtMxziF0STPvfgEZ/foyK9IF"
|
||||||
|
},
|
||||||
|
"peer": {
|
||||||
|
"privateKey": "47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a",
|
||||||
|
"nitroAddress": "0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d",
|
||||||
|
"nitroPrivateKey": "58368d20ff12f17669c06158c21d885897aa56f9be430edc789614bf9851d53f"
|
||||||
|
},
|
||||||
|
"consensus": {
|
||||||
|
"publicKey": "039160c244a7ad8be16a64bdb69e6dbacdcfe20b37076792a0d06032a8097468ca",
|
||||||
|
"privateKey": "8894685fe81001d75662b079905472699373967451d1255ee5fc669d0a09a9ca"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
watcher_keys_dir="/server/keys"
|
||||||
|
|
||||||
|
# Configure relay nodes to connect to
|
||||||
|
# Note: Assuming host ports for watchers (1-3) relay nodes
|
||||||
|
RELAY_NODES="[\"/ip4/127.0.0.1/tcp/9090/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-1.json)\", \"/ip4/127.0.0.1/tcp/9091/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-2.json)\", \"/ip4/127.0.0.1/tcp/9092/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-3.json)\"]"
|
||||||
|
|
||||||
|
# Configure watcher (1) Nitro account to make payments to
|
||||||
|
PAYMENT_NITRO_ADDRESS=$(jq -r '.peer.nitroAddress' ${watcher_keys_dir}/watcher-1.json)
|
||||||
|
|
||||||
|
echo "Using RELAY_NODES $RELAY_NODES"
|
||||||
|
echo "Using PAYMENT_NITRO_ADDRESS $PAYMENT_NITRO_ADDRESS"
|
||||||
|
|
||||||
|
# Use config from mounted volume
|
||||||
|
echo "Taking config for deployed contracts from mounted volume"
|
||||||
|
while [ ! -f /server/config.json ]; do
|
||||||
|
echo "Config not found, retrying in 5 seconds..."
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
# Get deployed contract address and chain id
|
||||||
|
DEPLOYED_CONTRACT=$(jq -r '.address' /server/config.json | tr -d '"')
|
||||||
|
CHAIN_ID=$(jq -r '.chainId' /server/config.json)
|
||||||
|
|
||||||
|
echo "Using DEPLOYED_CONTRACT $DEPLOYED_CONTRACT"
|
||||||
|
echo "Using CHAIN_ID $CHAIN_ID"
|
||||||
|
|
||||||
|
nitro_addresses_file="/nitro/nitro-addresses.json"
|
||||||
|
nitro_addresses_destination_file="/app/src/utils/nitro-addresses.json"
|
||||||
|
if [ -f ${nitro_addresses_file} ]; then
|
||||||
|
echo "Using Nitro addresses from ${nitro_addresses_file}:"
|
||||||
|
cat "$nitro_addresses_file"
|
||||||
|
cat "$nitro_addresses_file" > "$nitro_addresses_destination_file"
|
||||||
|
else
|
||||||
|
echo "Nitro addresses not available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Export config values in a json file
|
||||||
|
app_config_file="/app/src/utils/config.json"
|
||||||
|
app_config_json=$(jq -n \
|
||||||
|
--arg name "MobyMask" \
|
||||||
|
--argjson enableDebugInfo true \
|
||||||
|
--arg address "$DEPLOYED_CONTRACT" \
|
||||||
|
--argjson chainId "$CHAIN_ID" \
|
||||||
|
--argjson relayNodes "$RELAY_NODES" \
|
||||||
|
--argjson denyMultiaddrs "[]" \
|
||||||
|
--arg pubsub "" \
|
||||||
|
--argjson directPeers "[]" \
|
||||||
|
'.name = $name | .address = $address | .chainId = $chainId | .relayNodes = $relayNodes | .peer.enableDebugInfo = $enableDebugInfo | .peer.denyMultiaddrs = $denyMultiaddrs | .peer.pubsub = $pubsub | .peer.directPeers = $directPeers')
|
||||||
|
echo "$app_config_json" > "${app_config_file}"
|
||||||
|
|
||||||
|
echo "Using CERC_APP_WATCHER_URL $CERC_APP_WATCHER_URL"
|
||||||
|
echo "Using CERC_SNAP_URL $CERC_SNAP_URL"
|
||||||
|
|
||||||
|
REACT_APP_DEBUG_PEER=true \
|
||||||
|
REACT_APP_WATCHER_URI="$CERC_APP_WATCHER_URL/graphql" \
|
||||||
|
REACT_APP_PAY_TO_NITRO_ADDRESS="$PAYMENT_NITRO_ADDRESS" \
|
||||||
|
REACT_APP_SNAP_ORIGIN="local:$CERC_SNAP_URL" \
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
http-server -p 80 /app/build
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
watcher_keys_dir="./keys"
|
||||||
|
|
||||||
|
echo "Watcher index: ${CERC_WATCHER_INDEX}"
|
||||||
|
echo "Using RPC query endpoint ${CERC_ETH_RPC_QUERY_ENDPOINT}"
|
||||||
|
echo "Using RPC mutation endpoint ${CERC_ETH_RPC_MUTATION_ENDPOINT}"
|
||||||
|
echo "Using Nitro chain URL ${CERC_NITRO_CHAIN_URL}"
|
||||||
|
|
||||||
|
WATCHER_DB="mobymask-watcher-${CERC_WATCHER_INDEX}"
|
||||||
|
|
||||||
|
# Use the docker container's host IP for relay multiaddr in peer config
|
||||||
|
CERC_RELAY_MULTIADDR="/dns4/mobymask-watcher-${CERC_WATCHER_INDEX}-server/tcp/9090/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-${CERC_WATCHER_INDEX}.json)"
|
||||||
|
|
||||||
|
# Assign deployed contract address from server config (created by mobymask container after deploying contract)
|
||||||
|
CONTRACT_ADDRESS=$(jq -r '.address' /server/config.json | tr -d '"')
|
||||||
|
|
||||||
|
nitro_addresses_file="/nitro/nitro-addresses.json"
|
||||||
|
nitro_addresses_destination_file="./src/nitro-addresses.json"
|
||||||
|
if [ -f ${nitro_addresses_file} ]; then
|
||||||
|
echo "Using Nitro addresses from ${nitro_addresses_file}:"
|
||||||
|
cat "$nitro_addresses_file"
|
||||||
|
cat "$nitro_addresses_file" > "$nitro_addresses_destination_file"
|
||||||
|
else
|
||||||
|
echo "Nitro addresses not available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build after setting the Nitro addresses
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
PEER_ID_FILE='./peer-id.json'
|
||||||
|
RELAY_PEER_ID_FILE='./relay-id.json'
|
||||||
|
|
||||||
|
# Create watcher party array
|
||||||
|
watcher_parties=()
|
||||||
|
|
||||||
|
# Iterate over each fixture JSON file
|
||||||
|
for watcher_keys_file in "$watcher_keys_dir"/*.json; do
|
||||||
|
# Extract the filename without the path and extension
|
||||||
|
filename=$(basename "$watcher_keys_file" .json)
|
||||||
|
|
||||||
|
# Read the consensus keys
|
||||||
|
peer_id=$(jq -r '.peerId.id' "$watcher_keys_file")
|
||||||
|
|
||||||
|
# Read the consensus keys
|
||||||
|
consensus_public_key=$(jq -r '.consensus.publicKey' "$watcher_keys_file")
|
||||||
|
consensus_private_key=$(jq -r '.consensus.privateKey' "$watcher_keys_file")
|
||||||
|
|
||||||
|
# Append watcher party
|
||||||
|
watcher_party=$(jq -n \
|
||||||
|
--arg peerId "$peer_id" \
|
||||||
|
--arg publicKey "$consensus_public_key" \
|
||||||
|
'.peerId = $peerId | .publicKey = $publicKey')
|
||||||
|
watcher_parties+=("$watcher_party")
|
||||||
|
|
||||||
|
if [ "$filename" = "watcher-${CERC_WATCHER_INDEX}" ]; then
|
||||||
|
# Export watcher peer and relay ids
|
||||||
|
peer_id_data=$(jq '.peerId' "$watcher_keys_file")
|
||||||
|
relay_peer_id_data=$(jq '.relayPeerId' "$watcher_keys_file")
|
||||||
|
echo "$peer_id_data" > "${PEER_ID_FILE}"
|
||||||
|
echo "$relay_peer_id_data" > "${RELAY_PEER_ID_FILE}"
|
||||||
|
|
||||||
|
# Set peer and nitro account keys
|
||||||
|
CERC_PRIVATE_KEY_PEER=$(jq -r '.peer.privateKey' "$watcher_keys_file")
|
||||||
|
CERC_WATCHER_NITRO_PK=$(jq -r '.peer.nitroPrivateKey' "$watcher_keys_file")
|
||||||
|
|
||||||
|
# Set consensus keys for this peer
|
||||||
|
CONSENSUS_PUBLIC_KEY=${consensus_public_key}
|
||||||
|
CONSENSUS_PRIVATE_KEY=${consensus_private_key}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Consensus module enabled"
|
||||||
|
CONSENSUS_ENABLED=true
|
||||||
|
WATCHER_PARTY_PEERS_FILE='./watcher-party-peers.json'
|
||||||
|
|
||||||
|
# Export watcher party file
|
||||||
|
watcher_parties_json=$(printf '%s\n' "${watcher_parties[@]}" | jq -s .)
|
||||||
|
echo "$watcher_parties_json" > "${WATCHER_PARTY_PEERS_FILE}"
|
||||||
|
echo "Watcher party peers exported to ${WATCHER_PARTY_PEERS_FILE}"
|
||||||
|
|
||||||
|
# Configure relay peers for watcher 2 and 3
|
||||||
|
CERC_RELAY_PEERS=[]
|
||||||
|
if [ "${CERC_WATCHER_INDEX}" = "2" ]; then
|
||||||
|
# 2 -> 1
|
||||||
|
CERC_RELAY_PEERS="['/dns4/mobymask-watcher-1-server/tcp/9090/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-1.json)']"
|
||||||
|
elif [ "${CERC_WATCHER_INDEX}" = "3" ]; then
|
||||||
|
# 3 -> 1, 2
|
||||||
|
CERC_RELAY_PEERS="['/dns4/mobymask-watcher-1-server/tcp/9090/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-1.json)', '/dns4/mobymask-watcher-2-server/tcp/9090/ws/p2p/$(jq -r '.relayPeerId.id' ${watcher_keys_dir}/watcher-2.json)']"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read in the config template TOML file and modify it
|
||||||
|
WATCHER_CONFIG_TEMPLATE=$(cat environments/watcher-config-template.toml)
|
||||||
|
WATCHER_CONFIG=$(echo "$WATCHER_CONFIG_TEMPLATE" | \
|
||||||
|
sed -E "s|REPLACE_WITH_CERC_RELAY_PEERS|${CERC_RELAY_PEERS}|g; \
|
||||||
|
s|REPLACE_WITH_CERC_RELAY_MULTIADDR|${CERC_RELAY_MULTIADDR}|g; \
|
||||||
|
s/REPLACE_WITH_CERC_PRIVATE_KEY_PEER/${CERC_PRIVATE_KEY_PEER}/g; \
|
||||||
|
s/REPLACE_WITH_CERC_WATCHER_NITRO_PK/${CERC_WATCHER_NITRO_PK}/g; \
|
||||||
|
s/REPLACE_WITH_CONTRACT_ADDRESS/${CONTRACT_ADDRESS}/g; \
|
||||||
|
s|REPLACE_WITH_CERC_NITRO_CHAIN_URL|${CERC_NITRO_CHAIN_URL}|g; \
|
||||||
|
s/REPLACE_WITH_CONSENSUS_ENABLED/${CONSENSUS_ENABLED}/g; \
|
||||||
|
s/REPLACE_WITH_CONSENSUS_PUBLIC_KEY/${CONSENSUS_PUBLIC_KEY}/g; \
|
||||||
|
s/REPLACE_WITH_CONSENSUS_PRIVATE_KEY/${CONSENSUS_PRIVATE_KEY}/g; \
|
||||||
|
s|REPLACE_WITH_WATCHER_PARTY_PEERS_FILE|${WATCHER_PARTY_PEERS_FILE}|g; \
|
||||||
|
s|REPLACE_WITH_CERC_ETH_RPC_QUERY_ENDPOINT|${CERC_ETH_RPC_QUERY_ENDPOINT}|g; \
|
||||||
|
s|REPLACE_WITH_CERC_ETH_RPC_MUTATION_ENDPOINT|${CERC_ETH_RPC_MUTATION_ENDPOINT}|g; \
|
||||||
|
s|REPLACE_WITH_WATCHER_DB|${WATCHER_DB}| ")
|
||||||
|
|
||||||
|
# Write the modified content to a new file
|
||||||
|
echo "$WATCHER_CONFIG" > environments/local.toml
|
||||||
|
|
||||||
|
echo 'Running watcher server'
|
||||||
|
yarn server
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
freeQueriesLimit = 10
|
||||||
|
|
||||||
|
freeQueriesList = []
|
||||||
|
|
||||||
|
[queries]
|
||||||
|
multiNonce = '50'
|
||||||
|
_owner = '50'
|
||||||
|
isRevoked = '50'
|
||||||
|
isPhisher = '50'
|
||||||
|
isMember = '50'
|
||||||
|
|
||||||
|
[mutations]
|
||||||
|
invoke = '100'
|
||||||
|
revoke = '100'
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
[server]
|
||||||
|
host = "0.0.0.0"
|
||||||
|
port = 3001
|
||||||
|
kind = "lazy"
|
||||||
|
|
||||||
|
# Checkpointing state.
|
||||||
|
checkpointing = false
|
||||||
|
|
||||||
|
# Checkpoint interval in number of blocks.
|
||||||
|
checkpointInterval = 2000
|
||||||
|
|
||||||
|
# Enable state creation
|
||||||
|
enableState = false
|
||||||
|
|
||||||
|
# Boolean to filter logs by contract.
|
||||||
|
filterLogs = true
|
||||||
|
|
||||||
|
# Max block range for which to return events in eventsInRange GQL query.
|
||||||
|
# Use -1 for skipping check on block range.
|
||||||
|
maxEventsBlockRange = -1
|
||||||
|
|
||||||
|
# Flag to specify whether RPC endpoint supports block hash as block tag parameter
|
||||||
|
rpcSupportsBlockHashParam = true
|
||||||
|
|
||||||
|
[server.p2p]
|
||||||
|
enableRelay = true
|
||||||
|
enablePeer = true
|
||||||
|
|
||||||
|
[server.p2p.relay]
|
||||||
|
host = "0.0.0.0"
|
||||||
|
port = 9090
|
||||||
|
relayPeers = REPLACE_WITH_CERC_RELAY_PEERS
|
||||||
|
denyMultiaddrs = []
|
||||||
|
peerIdFile = './relay-id.json'
|
||||||
|
announce = ''
|
||||||
|
pubsub = ''
|
||||||
|
enableDebugInfo = true
|
||||||
|
|
||||||
|
[server.p2p.peer]
|
||||||
|
relayMultiaddr = 'REPLACE_WITH_CERC_RELAY_MULTIADDR'
|
||||||
|
pubSubTopic = 'mobymask'
|
||||||
|
denyMultiaddrs = []
|
||||||
|
peerIdFile = './peer-id.json'
|
||||||
|
pubsub = ''
|
||||||
|
enableDebugInfo = true
|
||||||
|
enableL2Txs = true
|
||||||
|
pingInterval = 4000
|
||||||
|
pingTimeout = 1500
|
||||||
|
maxRelayConnections = 10
|
||||||
|
|
||||||
|
[server.p2p.peer.l2TxsConfig]
|
||||||
|
privateKey = 'REPLACE_WITH_CERC_PRIVATE_KEY_PEER'
|
||||||
|
contractAddress = 'REPLACE_WITH_CONTRACT_ADDRESS'
|
||||||
|
|
||||||
|
[server.p2p.nitro]
|
||||||
|
chainUrl = 'REPLACE_WITH_CERC_NITRO_CHAIN_URL'
|
||||||
|
store = './out/nitro-db'
|
||||||
|
privateKey = 'REPLACE_WITH_CERC_WATCHER_NITRO_PK'
|
||||||
|
chainPrivateKey = 'REPLACE_WITH_CERC_PRIVATE_KEY_PEER'
|
||||||
|
|
||||||
|
[server.p2p.nitro.payments]
|
||||||
|
ratesFile = './environments/rates.toml'
|
||||||
|
requestTimeoutInSecs = 10
|
||||||
|
|
||||||
|
[server.p2p.nitro.payments.cache]
|
||||||
|
maxAccounts = 1000
|
||||||
|
accountTTLInSecs = 1800
|
||||||
|
maxVouchersPerAccount = 1000
|
||||||
|
voucherTTLInSecs = 300
|
||||||
|
maxPaymentChannels = 10000
|
||||||
|
paymentChannelTTLInSecs = 1800
|
||||||
|
|
||||||
|
[server.p2p.consensus]
|
||||||
|
enabled = REPLACE_WITH_CONSENSUS_ENABLED
|
||||||
|
publicKey = 'REPLACE_WITH_CONSENSUS_PUBLIC_KEY'
|
||||||
|
privateKey = 'REPLACE_WITH_CONSENSUS_PRIVATE_KEY'
|
||||||
|
watcherPartyPeersFile = 'REPLACE_WITH_WATCHER_PARTY_PEERS_FILE'
|
||||||
|
|
||||||
|
[metrics]
|
||||||
|
host = "0.0.0.0"
|
||||||
|
port = 9000
|
||||||
|
[metrics.gql]
|
||||||
|
port = 9001
|
||||||
|
|
||||||
|
[database]
|
||||||
|
type = "postgres"
|
||||||
|
host = "mobymask-watcher-db"
|
||||||
|
port = 5432
|
||||||
|
database = "REPLACE_WITH_WATCHER_DB"
|
||||||
|
username = "vdbm"
|
||||||
|
password = "password"
|
||||||
|
synchronize = true
|
||||||
|
logging = false
|
||||||
|
|
||||||
|
[upstream]
|
||||||
|
[upstream.ethServer]
|
||||||
|
gqlApiEndpoint = "http://ipld-eth-server:8083/graphql"
|
||||||
|
rpcProviderEndpoint = 'REPLACE_WITH_CERC_ETH_RPC_QUERY_ENDPOINT'
|
||||||
|
rpcProviderMutationEndpoint = 'REPLACE_WITH_CERC_ETH_RPC_MUTATION_ENDPOINT'
|
||||||
|
|
||||||
|
[upstream.cache]
|
||||||
|
name = "requests"
|
||||||
|
enabled = false
|
||||||
|
deleteOnStart = false
|
||||||
|
|
||||||
|
[jobQueue]
|
||||||
|
dbConnectionString = "postgres://vdbm:password@mobymask-watcher-db/REPLACE_WITH_WATCHER_DB-job-queue"
|
||||||
|
maxCompletionLagInSecs = 300
|
||||||
|
jobDelayInMilliSecs = 100
|
||||||
|
eventsInBatch = 50
|
||||||
|
blockDelayInMilliSecs = 60000
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the mars-v2 image
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
docker build -t cerc/mars-v2:local -f ${CERC_REPO_BASE_DIR}/mars-v2-frontend/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/mars-v2-frontend
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the mars image
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
docker build -t cerc/mars:local -f ${CERC_REPO_BASE_DIR}/mars-interface/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/mars-interface
|
||||||
+2
-2
@@ -33,8 +33,8 @@ if [ -f ".env" ]; then
|
|||||||
rm -f $TMP_ENV
|
rm -f $TMP_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for f in $(find "$TRG_DIR" -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do
|
for f in $(find . -type f \( -regex '.*.html?' -or -regex ".*.[tj]s\(x\|on\)?$" \) | grep -v 'node_modules' | grep -v '.git'); do
|
||||||
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},();]' '\n' | egrep -o '^"CERC_RUNTIME_ENV_[^\"]+"'); do
|
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '["/\\{},();]' '\n' | tr -s "[']" '\n' | egrep -o -e '^CERC_RUNTIME_ENV_.+$' -e '^LACONIC_HOSTED_CONFIG_.+$'); do
|
||||||
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
||||||
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
||||||
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ FROM cerc/foundry:local
|
|||||||
# Install node (local foundry is a debian based image)
|
# Install node (local foundry is a debian based image)
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y curl wget \
|
&& apt-get install -y curl wget \
|
||||||
&& curl --silent --location https://deb.nodesource.com/setup_16.x | bash - \
|
&& apt-get install -y git busybox jq python3 make gcc g++
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y nodejs git busybox jq \
|
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \
|
||||||
|
&& export NVM_DIR="$HOME/.nvm" \
|
||||||
|
&& [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" \
|
||||||
|
&& nvm install 16.16.0 \
|
||||||
&& node -v
|
&& node -v
|
||||||
|
|
||||||
RUN corepack enable \
|
SHELL ["/bin/bash", "-c"]
|
||||||
|
RUN source $HOME/.bashrc \
|
||||||
|
&& corepack enable \
|
||||||
&& yarn --version
|
&& yarn --version
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -16,7 +21,10 @@ WORKDIR /app
|
|||||||
# Copy optimism repo contents
|
# Copy optimism repo contents
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN echo "Building optimism" && \
|
SHELL ["/bin/bash", "-c"]
|
||||||
pnpm install && pnpm build
|
RUN source $HOME/.bashrc \
|
||||||
|
&& echo "Building optimism" \
|
||||||
|
&& pnpm i -g pnpm@8.15.3 \
|
||||||
|
&& pnpm install && pnpm build
|
||||||
|
|
||||||
WORKDIR /app/packages/contracts-bedrock
|
WORKDIR /app/packages/contracts-bedrock
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the ping pub image
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
|
||||||
|
docker build -t cerc/ping-pub:local ${build_command_args} -f $CERC_REPO_BASE_DIR/explorer/Dockerfile $CERC_REPO_BASE_DIR/explorer
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
FROM cerc/snowballtools-base-backend-base:local
|
||||||
|
|
||||||
|
WORKDIR /app/packages/backend
|
||||||
|
COPY run.sh .
|
||||||
|
|
||||||
|
ENTRYPOINT ["./run.sh"]
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
FROM ubuntu:22.04 as builder
|
||||||
|
|
||||||
|
RUN apt update && \
|
||||||
|
apt install -y --no-install-recommends --no-install-suggests \
|
||||||
|
ca-certificates curl gnupg
|
||||||
|
|
||||||
|
# Node
|
||||||
|
ARG NODE_MAJOR=20
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||||
|
apt update && apt install -y nodejs
|
||||||
|
|
||||||
|
# npm setup
|
||||||
|
RUN npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/ && npm install -g yarn
|
||||||
|
|
||||||
|
COPY . /app/
|
||||||
|
WORKDIR /app/
|
||||||
|
|
||||||
|
RUN find . -name 'node_modules' | xargs -n1 rm -rf
|
||||||
|
RUN yarn && yarn build --ignore frontend
|
||||||
|
|
||||||
|
FROM cerc/webapp-base:local
|
||||||
|
|
||||||
|
COPY --from=builder /app /app
|
||||||
|
|
||||||
|
WORKDIR /app/packages/backend
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/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/snowballtools-base-backend-base:local ${build_command_args} -f ${SCRIPT_DIR}/Dockerfile-base ${CERC_REPO_BASE_DIR}/snowballtools-base
|
||||||
|
docker build -t cerc/snowballtools-base-backend:local ${build_command_args} ${SCRIPT_DIR}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
LACONIC_HOSTED_CONFIG_FILE=${LACONIC_HOSTED_CONFIG_FILE}
|
||||||
|
if [ -z "${LACONIC_HOSTED_CONFIG_FILE}" ]; then
|
||||||
|
if [ -f "/config/laconic-hosted-config.yml" ]; then
|
||||||
|
LACONIC_HOSTED_CONFIG_FILE="/config/laconic-hosted-config.yml"
|
||||||
|
elif [ -f "/config/config.yml" ]; then
|
||||||
|
LACONIC_HOSTED_CONFIG_FILE="/config/config.yml"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${LACONIC_HOSTED_CONFIG_FILE}" ]; then
|
||||||
|
/scripts/apply-webapp-config.sh $LACONIC_HOSTED_CONFIG_FILE "`pwd`/dist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
/scripts/apply-runtime-env.sh "`pwd`/dist"
|
||||||
|
|
||||||
|
yarn start
|
||||||
@@ -1,22 +1,44 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
set -x
|
set -x
|
||||||
fi
|
fi
|
||||||
# Test if the container's filesystem is old (run previously) or new
|
|
||||||
EXISTSFILENAME=/data/exists
|
|
||||||
echo "Test container starting"
|
echo "Test container starting"
|
||||||
if [[ -f "$EXISTSFILENAME" ]];
|
|
||||||
then
|
DATA_DEVICE=$(df | grep "/data$" | awk '{ print $1 }')
|
||||||
TIMESTAMP=`cat $EXISTSFILENAME`
|
if [[ -n "$DATA_DEVICE" ]]; then
|
||||||
echo "Filesystem is old, created: $TIMESTAMP"
|
echo "/data: MOUNTED dev=${DATA_DEVICE}"
|
||||||
else
|
else
|
||||||
echo "Filesystem is fresh"
|
echo "/data: not mounted"
|
||||||
echo `date` > $EXISTSFILENAME
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
DATA2_DEVICE=$(df | grep "/data2$" | awk '{ print $1 }')
|
||||||
|
if [[ -n "$DATA_DEVICE" ]]; then
|
||||||
|
echo "/data2: MOUNTED dev=${DATA2_DEVICE}"
|
||||||
|
else
|
||||||
|
echo "/data2: not mounted"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test if the container's filesystem is old (run previously) or new
|
||||||
|
for d in /data /data2; do
|
||||||
|
if [[ -f "$d/exists" ]];
|
||||||
|
then
|
||||||
|
TIMESTAMP=`cat $d/exists`
|
||||||
|
echo "$d filesystem is old, created: $TIMESTAMP"
|
||||||
|
else
|
||||||
|
echo "$d filesystem is fresh"
|
||||||
|
echo `date` > $d/exists
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [ -n "$CERC_TEST_PARAM_1" ]; then
|
if [ -n "$CERC_TEST_PARAM_1" ]; then
|
||||||
echo "Test-param-1: ${CERC_TEST_PARAM_1}"
|
echo "Test-param-1: ${CERC_TEST_PARAM_1}"
|
||||||
fi
|
fi
|
||||||
|
if [ -n "$CERC_TEST_PARAM_2" ]; then
|
||||||
|
echo "Test-param-2: ${CERC_TEST_PARAM_2}"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -d "/config" ]; then
|
if [ -d "/config" ]; then
|
||||||
echo "/config: EXISTS"
|
echo "/config: EXISTS"
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
FROM ubuntu:latest
|
||||||
|
|
||||||
|
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && export DEBCONF_NOWARNINGS="yes" && \
|
||||||
|
apt-get install -y software-properties-common && \
|
||||||
|
apt-get install -y postgresql-client && \
|
||||||
|
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
COPY run.sh /app/run.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/run.sh"]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build cerc/test-container
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
docker build -t cerc/test-database-client:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} $SCRIPT_DIR
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO derive this from config
|
||||||
|
database_url="postgresql://test-user:password@localhost:5432/test-db"
|
||||||
|
psql_command="psql ${database_url}"
|
||||||
|
program_name="Database test client:"
|
||||||
|
|
||||||
|
wait_for_database_up () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
${psql_command} -c "select 1;"
|
||||||
|
psql_succeeded=$?
|
||||||
|
if [[ ${psql_succeeded} == 0 ]]; then
|
||||||
|
# if ready, return
|
||||||
|
echo "${program_name} database up"
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
echo "${program_name} waiting for database: ${i}"
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "${program_name} waiting for database: FAILED"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Used to synchronize with the test runner
|
||||||
|
notify_test_complete () {
|
||||||
|
echo "${program_name} test complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
does_test_data_exist () {
|
||||||
|
query_result=$(${psql_command} -t -c "select count(*) from test_table_1 where key_column = 'test_key_1';" | head -1 | tr -d ' ')
|
||||||
|
if [[ "${query_result}" == "1" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_test_data () {
|
||||||
|
${psql_command} -c "create table test_table_1 (key_column text, value_column text, primary key(key_column));"
|
||||||
|
${psql_command} -c "insert into test_table_1 values ('test_key_1', 'test_value_1');"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_forever() {
|
||||||
|
# Loop to keep docker/k8s happy since this is the container entrypoint
|
||||||
|
while :; do sleep 600; done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_database_up
|
||||||
|
|
||||||
|
# Check if the test database content exists already
|
||||||
|
if does_test_data_exist; then
|
||||||
|
# If so, log saying so. Test harness will look for this log output
|
||||||
|
echo "${program_name} test data already exists"
|
||||||
|
else
|
||||||
|
# Otherwise log saying the content was not present
|
||||||
|
echo "${program_name} test data does not exist"
|
||||||
|
echo "${program_name} creating test data"
|
||||||
|
# then create it
|
||||||
|
create_test_data
|
||||||
|
fi
|
||||||
|
|
||||||
|
notify_test_complete
|
||||||
|
wait_forever
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM postgres:16-bullseye
|
||||||
|
|
||||||
|
EXPOSE 5432
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build cerc/test-container
|
||||||
|
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
docker build -t cerc/test-database-container:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} $SCRIPT_DIR
|
||||||
@@ -43,7 +43,6 @@ COPY scripts /scripts
|
|||||||
# RUN su node -c "npm install -g <your-package-list-here>"
|
# RUN su node -c "npm install -g <your-package-list-here>"
|
||||||
|
|
||||||
RUN mkdir -p /config
|
RUN mkdir -p /config
|
||||||
COPY ./config.yml /config
|
|
||||||
|
|
||||||
# Install simple web server for now (use nginx perhaps later)
|
# Install simple web server for now (use nginx perhaps later)
|
||||||
RUN yarn global add http-server
|
RUN yarn global add http-server
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# Put config here.
|
|
||||||
+2
-2
@@ -18,8 +18,8 @@ if [ -f ".env" ]; then
|
|||||||
rm -f $TMP_ENV
|
rm -f $TMP_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for f in $(find . -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do
|
for f in $(find . -type f \( -regex '.*.html?' -or -regex ".*.[tj]s\(x\|on\)?$" \) | grep -v 'node_modules' | grep -v '.git'); do
|
||||||
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},();]' '\n' | egrep -o -e '^"CERC_RUNTIME_ENV_[^\"]+"' -e '^"LACONIC_HOSTED_CONFIG_[^\"]+"'); do
|
for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '["/\\{},();]' '\n' | tr -s "[']" '\n' | egrep -o -e '^CERC_RUNTIME_ENV_.+$' -e '^LACONIC_HOSTED_CONFIG_.+$'); do
|
||||||
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
orig_name=$(echo -n "${e}" | sed 's/"//g')
|
||||||
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g')
|
||||||
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
cur_val=$(echo -n "\$${cur_name}" | envsubst)
|
||||||
|
|||||||
@@ -11,7 +11,13 @@ WORK_DIR="${1:-/app}"
|
|||||||
OUTPUT_DIR="${2:-build}"
|
OUTPUT_DIR="${2:-build}"
|
||||||
DEST_DIR="${3:-/data}"
|
DEST_DIR="${3:-/data}"
|
||||||
|
|
||||||
if [ -f "${WORK_DIR}/package.json" ]; then
|
if [ -f "${WORK_DIR}/build-webapp.sh" ]; then
|
||||||
|
echo "Building webapp with ${WORK_DIR}/build-webapp.sh ..."
|
||||||
|
cd "${WORK_DIR}" || exit 1
|
||||||
|
|
||||||
|
rm -rf "${DEST_DIR}"
|
||||||
|
./build-webapp.sh "${DEST_DIR}" || exit 1
|
||||||
|
elif [ -f "${WORK_DIR}/package.json" ]; then
|
||||||
echo "Building node-based webapp ..."
|
echo "Building node-based webapp ..."
|
||||||
cd "${WORK_DIR}" || exit 1
|
cd "${WORK_DIR}" || exit 1
|
||||||
|
|
||||||
@@ -33,4 +39,10 @@ else
|
|||||||
mv "${WORK_DIR}" "${DEST_DIR}"
|
mv "${WORK_DIR}" "${DEST_DIR}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# One special fix ...
|
||||||
|
cd "${DEST_DIR}"
|
||||||
|
for f in $(find . -type f -name '*.htm*'); do
|
||||||
|
sed -i -e 's#/LACONIC_HOSTED_CONFIG_homepage/#LACONIC_HOSTED_CONFIG_homepage/#g' "$f"
|
||||||
|
done
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
+13
-1
@@ -10,6 +10,18 @@ if [ "true" == "$CERC_ENABLE_CORS" ]; then
|
|||||||
CERC_HTTP_EXTRA_ARGS="$CERC_HTTP_EXTRA_ARGS --cors"
|
CERC_HTTP_EXTRA_ARGS="$CERC_HTTP_EXTRA_ARGS --cors"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/scripts/apply-webapp-config.sh /config/config.yml ${CERC_WEBAPP_FILES_DIR}
|
LACONIC_HOSTED_CONFIG_FILE=${LACONIC_HOSTED_CONFIG_FILE}
|
||||||
|
if [ -z "${LACONIC_HOSTED_CONFIG_FILE}" ]; then
|
||||||
|
if [ -f "/config/laconic-hosted-config.yml" ]; then
|
||||||
|
LACONIC_HOSTED_CONFIG_FILE="/config/laconic-hosted-config.yml"
|
||||||
|
elif [ -f "/config/config.yml" ]; then
|
||||||
|
LACONIC_HOSTED_CONFIG_FILE="/config/config.yml"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${LACONIC_HOSTED_CONFIG_FILE}" ]; then
|
||||||
|
/scripts/apply-webapp-config.sh $LACONIC_HOSTED_CONFIG_FILE ${CERC_WEBAPP_FILES_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
/scripts/apply-runtime-env.sh ${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}
|
http-server $CERC_HTTP_EXTRA_ARGS -p ${CERC_LISTEN_PORT:-80} ${CERC_WEBAPP_FILES_DIR}
|
||||||
|
|||||||
@@ -0,0 +1,673 @@
|
|||||||
|
# from: https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
|
||||||
|
# via: https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
name: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- namespaces
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- pods
|
||||||
|
- secrets
|
||||||
|
- endpoints
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingressclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resourceNames:
|
||||||
|
- ingress-nginx-leader
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- discovery.k8s.io
|
||||||
|
resources:
|
||||||
|
- endpointslices
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- endpoints
|
||||||
|
- nodes
|
||||||
|
- pods
|
||||||
|
- secrets
|
||||||
|
- namespaces
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- coordination.k8s.io
|
||||||
|
resources:
|
||||||
|
- leases
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingressclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- discovery.k8s.io
|
||||||
|
resources:
|
||||||
|
- endpointslices
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- admissionregistration.k8s.io
|
||||||
|
resources:
|
||||||
|
- validatingwebhookconfigurations
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: ingress-nginx
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: ingress-nginx
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
allow-snippet-annotations: "false"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
ipFamilies:
|
||||||
|
- IPv4
|
||||||
|
ipFamilyPolicy: SingleStack
|
||||||
|
ports:
|
||||||
|
- appProtocol: http
|
||||||
|
name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: http
|
||||||
|
- appProtocol: https
|
||||||
|
name: https
|
||||||
|
port: 443
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: https
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
type: NodePort
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- appProtocol: https
|
||||||
|
name: https-webhook
|
||||||
|
port: 443
|
||||||
|
targetPort: webhook
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
type: ClusterIP
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-controller
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 0
|
||||||
|
revisionHistoryLimit: 10
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
strategy:
|
||||||
|
rollingUpdate:
|
||||||
|
maxUnavailable: 1
|
||||||
|
type: RollingUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- /nginx-ingress-controller
|
||||||
|
- --election-id=ingress-nginx-leader
|
||||||
|
- --controller-class=k8s.io/ingress-nginx
|
||||||
|
- --ingress-class=nginx
|
||||||
|
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
|
||||||
|
- --validating-webhook=:8443
|
||||||
|
- --validating-webhook-certificate=/usr/local/certificates/cert
|
||||||
|
- --validating-webhook-key=/usr/local/certificates/key
|
||||||
|
- --watch-ingress-without-class=true
|
||||||
|
- --publish-status-address=localhost
|
||||||
|
env:
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: LD_PRELOAD
|
||||||
|
value: /usr/local/lib/libmimalloc.so
|
||||||
|
image: registry.k8s.io/ingress-nginx/controller:v1.9.6@sha256:1405cc613bd95b2c6edd8b2a152510ae91c7e62aea4698500d23b2145960ab9c
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /wait-shutdown
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 1
|
||||||
|
name: controller
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
hostPort: 80
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 443
|
||||||
|
hostPort: 443
|
||||||
|
name: https
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 8443
|
||||||
|
name: webhook
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
failureThreshold: 3
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 1
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 90Mi
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
add:
|
||||||
|
- NET_BIND_SERVICE
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: false
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 101
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /usr/local/certificates/
|
||||||
|
name: webhook-cert
|
||||||
|
readOnly: true
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
nodeSelector:
|
||||||
|
ingress-ready: "true"
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
serviceAccountName: ingress-nginx
|
||||||
|
terminationGracePeriodSeconds: 0
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: node-role.kubernetes.io/master
|
||||||
|
operator: Equal
|
||||||
|
- effect: NoSchedule
|
||||||
|
key: node-role.kubernetes.io/control-plane
|
||||||
|
operator: Equal
|
||||||
|
volumes:
|
||||||
|
- name: webhook-cert
|
||||||
|
secret:
|
||||||
|
secretName: ingress-nginx-admission
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-create
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-create
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- create
|
||||||
|
- --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
|
||||||
|
- --namespace=$(POD_NAMESPACE)
|
||||||
|
- --secret-name=ingress-nginx-admission
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231226-1a7112e06@sha256:25d6a5f11211cc5c3f9f2bf552b585374af287b4debf693cacbe2da47daa5084
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: create
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 65532
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
serviceAccountName: ingress-nginx-admission
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-patch
|
||||||
|
namespace: ingress-nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission-patch
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- patch
|
||||||
|
- --webhook-name=ingress-nginx-admission
|
||||||
|
- --namespace=$(POD_NAMESPACE)
|
||||||
|
- --patch-mutating=false
|
||||||
|
- --secret-name=ingress-nginx-admission
|
||||||
|
- --patch-failure-policy=Fail
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231226-1a7112e06@sha256:25d6a5f11211cc5c3f9f2bf552b585374af287b4debf693cacbe2da47daa5084
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: patch
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 65532
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
serviceAccountName: ingress-nginx-admission
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: IngressClass
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
controller: k8s.io/ingress-nginx
|
||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: admission-webhook
|
||||||
|
app.kubernetes.io/instance: ingress-nginx
|
||||||
|
app.kubernetes.io/name: ingress-nginx
|
||||||
|
app.kubernetes.io/part-of: ingress-nginx
|
||||||
|
app.kubernetes.io/version: 1.9.6
|
||||||
|
name: ingress-nginx-admission
|
||||||
|
webhooks:
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: ingress-nginx-controller-admission
|
||||||
|
namespace: ingress-nginx
|
||||||
|
path: /networking/v1/ingresses
|
||||||
|
failurePolicy: Fail
|
||||||
|
matchPolicy: Equivalent
|
||||||
|
name: validate.nginx.ingress.kubernetes.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
sideEffects: None
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Container Registry Stack
|
||||||
|
|
||||||
|
Host a container image registry
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
version: "1.0"
|
||||||
|
name: container-registry
|
||||||
|
description: "Container registry stack"
|
||||||
|
pods:
|
||||||
|
- container-registry
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# fixturenet-eth
|
# fixturenet-eth
|
||||||
|
|
||||||
Instructions for deploying a local a geth + lighthouse blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator (the installation of which is covered [here](https://github.com/cerc-io/stack-orchestrator)):
|
Instructions for deploying a local a geth + lighthouse blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator (the installation of which is covered [here](https://git.vdb.to/cerc-io/stack-orchestrator)):
|
||||||
|
|
||||||
## Clone required repositories
|
## Clone required repositories
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ Instructions for deploying a local Laconic blockchain "fixturenet" for developme
|
|||||||
**Note:** For building some NPMs, access to the @lirewine repositories is required. If you don't have access, see [this tutorial](/docs/laconicd-fixturenet.md) to run this stack
|
**Note:** For building some NPMs, access to the @lirewine repositories is required. If you don't have access, see [this tutorial](/docs/laconicd-fixturenet.md) to run this stack
|
||||||
|
|
||||||
## 1. Install Laconic Stack Orchestrator
|
## 1. Install Laconic Stack Orchestrator
|
||||||
Installation is covered in detail [here](https://github.com/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as:
|
Installation is covered in detail [here](https://git.vdb.to/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as:
|
||||||
```
|
```
|
||||||
$ mkdir my-working-dir
|
$ mkdir my-working-dir
|
||||||
$ cd my-working-dir
|
$ cd my-working-dir
|
||||||
$ curl -L -o ./laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so
|
$ curl -L -o ./laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so
|
||||||
$ chmod +x ./laconic-so
|
$ chmod +x ./laconic-so
|
||||||
$ export PATH=$PATH:$(pwd) # Or move laconic-so to ~/bin or your favorite on-path directory
|
$ export PATH=$PATH:$(pwd) # Or move laconic-so to ~/bin or your favorite on-path directory
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
Instructions for deploying a local Laconic blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator.
|
Instructions for deploying a local Laconic blockchain "fixturenet" for development and testing purposes using laconic-stack-orchestrator.
|
||||||
|
|
||||||
## 1. Install Laconic Stack Orchestrator
|
## 1. Install Laconic Stack Orchestrator
|
||||||
Installation is covered in detail [here](https://github.com/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as:
|
Installation is covered in detail [here](https://git.vdb.to/cerc-io/stack-orchestrator#user-mode) but if you're on Linux and already have docker installed it should be as simple as:
|
||||||
```
|
```
|
||||||
$ mkdir my-working-dir
|
$ mkdir my-working-dir
|
||||||
$ cd my-working-dir
|
$ cd my-working-dir
|
||||||
$ curl -L -o ./laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so
|
$ curl -L -o ./laconic-so https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so
|
||||||
$ chmod +x ./laconic-so
|
$ chmod +x ./laconic-so
|
||||||
$ export PATH=$PATH:$(pwd) # Or move laconic-so to ~/bin or your favorite on-path directory
|
$ export PATH=$PATH:$(pwd) # Or move laconic-so to ~/bin or your favorite on-path directory
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# mars
|
||||||
|
|
||||||
|
On a fresh Digital Ocean droplet with Ubuntu:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/cerc-io/stack-orchestrator
|
||||||
|
cd stack-orchestrator
|
||||||
|
./scripts/quick-install-linux.sh
|
||||||
|
```
|
||||||
|
Read and follow the instructions output from the above output to complete installation, then:
|
||||||
|
|
||||||
|
```
|
||||||
|
laconic-so --stack mars-v2 setup-repositories
|
||||||
|
laconic-so --stack mars-v2 build-containers
|
||||||
|
laconic-so --stack mars-v2 deploy up
|
||||||
|
```
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
version: "0.1"
|
||||||
|
name: mars-v2
|
||||||
|
repos:
|
||||||
|
- github.com/mars-protocol/mars-v2-frontend
|
||||||
|
containers:
|
||||||
|
- cerc/mars-v2
|
||||||
|
pods:
|
||||||
|
- mars-v2
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# mars
|
||||||
|
|
||||||
|
On a fresh Digital Ocean droplet with Ubuntu:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/cerc-io/stack-orchestrator
|
||||||
|
cd stack-orchestrator
|
||||||
|
./scripts/quick-install-linux.sh
|
||||||
|
```
|
||||||
|
Read and follow the instructions output from the above output to complete installation, then:
|
||||||
|
|
||||||
|
```
|
||||||
|
laconic-so --stack mars setup-repositories
|
||||||
|
laconic-so --stack mars build-containers
|
||||||
|
laconic-so --stack mars deploy up
|
||||||
|
```
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
version: "0.1"
|
||||||
|
name: mars
|
||||||
|
repos:
|
||||||
|
- github.com/cerc-io/mars-interface
|
||||||
|
containers:
|
||||||
|
- cerc/mars
|
||||||
|
pods:
|
||||||
|
- mars
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# MobyMask v3 Demo
|
||||||
|
|
||||||
|
Instructions to setup and deploy an end-to-end MobyMask v3 stack (fixturenet-optimism + watchers + web-app) locally using [laconic-stack-orchestrator](/README.md#install)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
* Clone required repositories:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so --stack mobymask-v3-demo setup-repositories --pull
|
||||||
|
```
|
||||||
|
|
||||||
|
* Build the container images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so --stack mobymask-v3-demo build-containers
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install MetaMask Flask extension in a chromium browser from chrome web store and follow the setup instructions
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
* Create a spec file for the deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so --stack mobymask-v3-demo deploy init --output mobymask-v3-demo-spec.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create a deployment from the generated spec file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so --stack mobymask-v3-demo deploy create --spec-file mobymask-v3-demo-spec.yml --deployment-dir mobymask-v3-demo-deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
* Copy over the demo config to place it at an appropriate path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp mobymask-v3-demo-deployment/config/watcher-mobymask-v3-demo/local/config.env mobymask-v3-demo-deployment/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start the stack
|
||||||
|
|
||||||
|
* Start the deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so deployment --dir mobymask-v3-demo-deployment start
|
||||||
|
|
||||||
|
# Useful ports exposed on host
|
||||||
|
# 3001: MobyMask v3 watcher 1 GQL endpoint
|
||||||
|
# 9090: MobyMask v3 watcher 1 relay node endpoint
|
||||||
|
# 9091: MobyMask v3 watcher 2 relay node endpoint
|
||||||
|
# 9092: MobyMask v3 watcher 3 relay node endpoint
|
||||||
|
# 8080: MobyMask snap
|
||||||
|
# 3004: MobyMask v3 app
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: This may take several minutes as it configures and runs the L1, L2 chains and the watchers; you can follow the progress from containers' logs
|
||||||
|
|
||||||
|
* To list and monitor the running containers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so deployment --dir mobymask-v3-demo-deployment ps
|
||||||
|
|
||||||
|
# With status
|
||||||
|
docker ps
|
||||||
|
|
||||||
|
# Check logs for a container
|
||||||
|
docker logs -f <CONTAINER_ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
Follow [demo](./demo.md) to try out the MobyMask app
|
||||||
|
|
||||||
|
## Clean up
|
||||||
|
|
||||||
|
To stop all services running in the background, while preserving data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so deployment --dir mobymask-v3-demo-deployment stop
|
||||||
|
```
|
||||||
|
|
||||||
|
To stop all services and also delete data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconic-so deployment --dir mobymask-v3-demo-deployment stop --delete-volumes
|
||||||
|
|
||||||
|
# Also remove the deployment directory
|
||||||
|
rm -rf mobymask-v3-demo-deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
* Resource requirements (memory + time) for building the `cerc/foundry` image are on the higher side
|
||||||
|
* `cerc/optimism-contracts` image is currently based on `cerc/foundry` (Optimism requires foundry installation)
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
# Demo
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
* Follow logs and check that all 3 watchers are running in consensus:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Follow the logs in three different terminals and keep them running
|
||||||
|
docker logs -f $(docker ps --filter "name=mobymask-watcher-1" -q)
|
||||||
|
docker logs -f $(docker ps --filter "name=mobymask-watcher-2" -q)
|
||||||
|
docker logs -f $(docker ps --filter "name=mobymask-watcher-3" -q)
|
||||||
|
|
||||||
|
# Expected output when all three are running in consensus:
|
||||||
|
# ...
|
||||||
|
# 2024-02-21T10:42:23.932Z laconic:consensus State changed to 3 (FOLLOWER) with term 286
|
||||||
|
# 2024-02-21T10:42:23.932Z laconic:consensus State changed to 2 (CANDIDATE) with term 287
|
||||||
|
# 2024-02-21T10:42:24.406Z laconic:consensus State changed to 1 (LEADER) with term 287
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# At any moment, only one of watchers is the 'LEADER'
|
||||||
|
```
|
||||||
|
|
||||||
|
* In MetaMask, go to settings add a custom network with the following settings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Network name
|
||||||
|
Local Optimism
|
||||||
|
|
||||||
|
# New RPC URL
|
||||||
|
http://127.0.0.1:8545
|
||||||
|
|
||||||
|
# Chain ID
|
||||||
|
42069
|
||||||
|
|
||||||
|
# Currency symbol
|
||||||
|
ETH
|
||||||
|
```
|
||||||
|
|
||||||
|
Switch to the newly added network
|
||||||
|
|
||||||
|
* Import a pre-funded account (using it's private key) for Nitro client in the MobyMask app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PK: 689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd
|
||||||
|
# Address: 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
### Open MobyMask app
|
||||||
|
|
||||||
|
* Copy the generated invite link from MobyMask deployment container logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs -f $(docker ps -a --filter "name=mobymask-1" -q)
|
||||||
|
|
||||||
|
# A SIGNED DELEGATION/INVITE LINK:
|
||||||
|
# ...
|
||||||
|
# http://127.0.0.1:3004/#/members?invitation=<INVITATION>
|
||||||
|
```
|
||||||
|
|
||||||
|
* Open the invite link in browser where MetaMask was setup
|
||||||
|
|
||||||
|
* In the app’s debug panel (bottom-right), check from `PEERS` and `GRAPH` tabs that the peer gets connected to relay nodes and watcher peers
|
||||||
|
|
||||||
|
* Perform phisher status checks from the app
|
||||||
|
* First 10 queries are served for free; repeat until the free quota is exhausted
|
||||||
|
* Same can be seen in the watcher-1's logs (the app makes all the GQL queries to watcher-1):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ...
|
||||||
|
# 2024-02-21T11:01:20.084Z laconic:payments Query rate not configured for "latestBlock", serving free query
|
||||||
|
# 2024-02-21T11:01:20.084Z vulcanize:resolver latestBlock
|
||||||
|
# 2024-02-21T11:01:20.108Z laconic:payments Serving a free query to 0x3c9B491ACA5cf17B6C11E39bbFddCA603F387d41
|
||||||
|
# 2024-02-21T11:01:20.109Z vulcanize:resolver isPhisher 0xf8995f83bbab2bc13fb9a43c6fea4a605616e6f59503f36cbe2ad3abffd0efd3 0xAFA36c47E130d89bcE4470a9030d99f3CEcaD146 TWT:dummyPhisher
|
||||||
|
# 2024-02-21T11:01:20.113Z vulcanize:indexer isPhisher: db miss, fetching from upstream server
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# After free quota has been exhausted:
|
||||||
|
# 2024-02-21T11:06:49.311Z laconic:payments Query rate not configured for "latestBlock", serving free query
|
||||||
|
# 2024-02-21T11:06:49.312Z vulcanize:resolver latestBlock
|
||||||
|
# 2024-02-21T11:06:49.337Z laconic:payments Rejecting query from 0x3c9B491ACA5cf17B6C11E39bbFddCA603F387d41: Free quota exhausted
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup app's Nitro node
|
||||||
|
|
||||||
|
* Open the `NITRO` tab in debug panel
|
||||||
|
|
||||||
|
* Click on `Connect Wallet` button to connect to MetaMask (use the imported account with funds)
|
||||||
|
|
||||||
|
* Click on `Connect Snap` to install / connect snap; the watcher Nitro clients should show up in the `NITRO` tab
|
||||||
|
|
||||||
|
* Click on `DIRECT FUND` button against watcher-1's Nitro account (`0xAAA6628Ec44A8a742987EF3A114dDFE2D4F7aDCE`) to create a ledger channel with the pre-set amount
|
||||||
|
* Confirm the tx in MetaMask popup; wait some time for the tx to be confirmed and ledger channel to be created
|
||||||
|
* The progress can be followed from watcher-1's logs
|
||||||
|
* The created ledger channel should now be visible in the `NITRO` tab; click on `REFRESH` button otherwise
|
||||||
|
|
||||||
|
* Change amount to `10000` and click on `VIRTUAL FUND` button to create a virtual payment channel
|
||||||
|
* This results in a payment channel between the app and watcher-1
|
||||||
|
* The payment channel's details should now be visible along with a `PAY` and `VIRTUAL DEFUND` buttons
|
||||||
|
|
||||||
|
* Close the debug panel
|
||||||
|
|
||||||
|
### Paid queries and mutations
|
||||||
|
|
||||||
|
* Perform phisher status checks now that a payment channel has been created
|
||||||
|
* Amount set in the debug panel's `NITRO` tab is sent along with each request to the watcher
|
||||||
|
* Check the watcher-1 logs for the received payments:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ...
|
||||||
|
# 2024-02-21T11:35:25.538Z ts-nitro:engine {"msg":"Received message","_msg":{"to":"0xAAA662","from":"0x5D12ac","payloadSummaries":[],"proposalSummaries":[],"payments":[{"amount":50,"channelId":"0x654a85725442828f89b497e3973640613c03b5f5ec47302bfa4402d42c07de30"}],"rejectedObjectives":[]}}
|
||||||
|
# 2024-02-21T11:35:25.552Z laconic:payments Query rate not configured for "latestBlock", serving free query
|
||||||
|
# 2024-02-21T11:35:25.553Z vulcanize:resolver latestBlock
|
||||||
|
# 2024-02-21T11:35:25.557Z laconic:payments Received a payment voucher of 50 from 0x5D12acfbBB1caD65fD61983003a50E0CB6900Fd3
|
||||||
|
# 2024-02-21T11:35:25.570Z laconic:payments Serving a paid query for 0x5D12acfbBB1caD65fD61983003a50E0CB6900Fd3
|
||||||
|
# 2024-02-21T11:35:25.570Z vulcanize:resolver isPhisher 0x6a1f0dce967aefd4adf7762c523cde358960236f05734f616ebe69c0abfcb0cc 0xAFA36c47E130d89bcE4470a9030d99f3CEcaD146 TWT:dummyPhisher
|
||||||
|
# 2024-02-21T11:35:25.580Z vulcanize:indexer isPhisher: db miss, fetching from upstream server
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
* Rate for mutations is set to `100` in the watcher; go back to the `NITRO` tab in the debug panel and change amount value besides `PAY` button to >=100
|
||||||
|
|
||||||
|
* Perform a phisher report
|
||||||
|
* Enter a new record(s) and click on `Submit batch to p2p network` button
|
||||||
|
* Among all three watchers running in consensus, whoever is the `LEADER` at time of reporting sends a tx to the chain
|
||||||
|
* Check all the watchers' logs:
|
||||||
|
|
||||||
|
If the payment receiving watcher (1) is leader at that moment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On watcher-1 (payment received + tx sent)
|
||||||
|
# ...
|
||||||
|
# 2024-02-21T11:42:55.088Z vulcanize:libp2p-utils [11:42:55] Received a message on mobymask P2P network from peer: 12D3KooWGXxcwevUY7KCfw8fcGhxqxPaiFMGSU4tgJDjE54QGKzf
|
||||||
|
# 2024-02-21T11:42:55.102Z ts-nitro:engine {"msg":"Received message","_msg":{"to":"0xAAA662","from":"0x5D12ac","payloadSummaries":[],"proposalSummaries":[],"payments":[{"amount":150,"channelId":"0x654a85725442828f89b497e3973640613c03b5f5ec47302bfa4402d42c07de30"}],"rejectedObjectives":[]}}
|
||||||
|
# 2024-02-21T11:42:55.115Z laconic:payments Received a payment voucher of 100 from 0x5D12acfbBB1caD65fD61983003a50E0CB6900Fd3
|
||||||
|
# 2024-02-21T11:42:55.115Z vulcanize:libp2p-utils Payment received for a mutation request from 0x5D12acfbBB1caD65fD61983003a50E0CB6900Fd3
|
||||||
|
# 2024-02-21T11:42:59.115Z vulcanize:libp2p-utils Transaction receipt for invoke message {
|
||||||
|
# to: '0xAFA36c47E130d89bcE4470a9030d99f3CEcaD146',
|
||||||
|
# blockNumber: 4638,
|
||||||
|
# blockHash: '0x23a42bc2ae43771c62b0d59cc48b5858e6e2e488953527ba6a9f5119ae72b42a',
|
||||||
|
# transactionHash: '0xb61cfdfd0ffe937a191a230e1355b9bc5cdd32507f7b4f24a8e6356adf089b64',
|
||||||
|
# effectiveGasPrice: '1500000050',
|
||||||
|
# gasUsed: '136450'
|
||||||
|
# }
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# On other watchers (payment not received + tx not sent)
|
||||||
|
...
|
||||||
|
# 2024-02-21T11:42:50.329Z laconic:consensus State changed to 3 (FOLLOWER) with term 478
|
||||||
|
# 2024-02-21T11:42:55.089Z vulcanize:libp2p-utils [11:42:55] Received a message on mobymask P2P network from peer: 12D3KooWGXxcwevUY7KCfw8fcGhxqxPaiFMGSU4tgJDjE54QGKzf
|
||||||
|
# 2024-02-21T11:42:55.096Z vulcanize:libp2p-utils Not a leader, skipped sending L2 tx
|
||||||
|
# 2024-02-21T11:43:05.095Z vulcanize:libp2p-utils Payment not received
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
If the payment receiving watcher (1) is NOT leader at that moment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On watcher 1 (payment received + tx not sent)
|
||||||
|
# ...
|
||||||
|
# 2024-02-21T11:46:52.049Z vulcanize:libp2p-utils [11:46:52] Received a message on mobymask P2P network from peer: 12D3KooWGXxcwevUY7KCfw8fcGhxqxPaiFMGSU4tgJDjE54QGKzf
|
||||||
|
# 2024-02-21T11:46:52.051Z vulcanize:libp2p-utils Not a leader, skipped sending L2 tx
|
||||||
|
# 2024-02-21T11:46:52.074Z ts-nitro:engine {"msg":"Received message","_msg":{"to":"0xAAA662","from":"0x5D12ac","payloadSummaries":[],"proposalSummaries":[],"payments":[{"amount":450,"channelId":"0x654a85725442828f89b497e3973640613c03b5f5ec47302bfa4402d42c07de30"}],"rejectedObjectives":[]}}
|
||||||
|
# 2024-02-21T11:46:52.097Z laconic:payments Received a payment voucher of 100 from 0x5D12acfbBB1caD65fD61983003a50E0CB6900Fd3
|
||||||
|
# 2024-02-21T11:46:52.097Z vulcanize:libp2p-utils Payment received for a mutation request from 0x5D12acfbBB1caD65fD61983003a50E0CB6900Fd3
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# On the leader watcher (payment not received + tx sent)
|
||||||
|
# ...
|
||||||
|
# 2024-02-21T11:46:40.111Z vulcanize:libp2p-utils Payment not received
|
||||||
|
# 2024-02-21T11:46:52.048Z vulcanize:libp2p-utils [11:46:52] Received a message on mobymask P2P network from peer: 12D3KooWGXxcwevUY7KCfw8fcGhxqxPaiFMGSU4tgJDjE54QGKzf
|
||||||
|
# 2024-02-21T11:46:56.048Z vulcanize:libp2p-utils Transaction receipt for invoke message {
|
||||||
|
# to: '0xAFA36c47E130d89bcE4470a9030d99f3CEcaD146',
|
||||||
|
# blockNumber: 4757,
|
||||||
|
# blockHash: '0x859aa54cb02e8a3f910a01b85c2a7bf9bff7540e83018e7e846f87ca1770e55e',
|
||||||
|
# transactionHash: '0x92f087e4b6ac5604c9d4ecd823d526ca5f8f1bf5c2a92861d6b0f69bd899ba83',
|
||||||
|
# effectiveGasPrice: '1500000050',
|
||||||
|
# gasUsed: '136438'
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
* Check phisher status for the reported phishers to confirm state update
|
||||||
|
|
||||||
|
## Clean up
|
||||||
|
|
||||||
|
* From the `NITRO` tab in debug panel, perform `VIRTUAL DEFUND` and `DIRECT DEFUND` (in order) for any payment channels created
|
||||||
|
|
||||||
|
* In the browser's console, delete all indexedDBs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
await clearNodeStorage()
|
||||||
|
```
|
||||||
|
|
||||||
|
* Remove the snap from MetaMask flask extension
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Copyright © 2023 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.deploy.deployment_context import DeploymentContext
|
||||||
|
from ruamel.yaml import YAML
|
||||||
|
|
||||||
|
|
||||||
|
def create(context: DeploymentContext, extra_args):
|
||||||
|
fixturenet_eth_compose_file = context.deployment_dir.joinpath('compose', 'docker-compose-fixturenet-eth.yml')
|
||||||
|
|
||||||
|
with open(fixturenet_eth_compose_file, 'r') as yaml_file:
|
||||||
|
yaml = YAML()
|
||||||
|
yaml_data = yaml.load(yaml_file)
|
||||||
|
|
||||||
|
new_script = '../config/fixturenet-optimism/run-geth.sh:/opt/testnet/run.sh'
|
||||||
|
if new_script not in yaml_data['services']['fixturenet-eth-geth-1']['volumes']:
|
||||||
|
yaml_data['services']['fixturenet-eth-geth-1']['volumes'].append(new_script)
|
||||||
|
|
||||||
|
with open(fixturenet_eth_compose_file, 'w') as yaml_file:
|
||||||
|
yaml = YAML()
|
||||||
|
yaml.dump(yaml_data, yaml_file)
|
||||||
|
|
||||||
|
# load fixturenet-optimism compose file
|
||||||
|
fixturenet_optimism_compose_file = context.deployment_dir.joinpath(
|
||||||
|
"compose",
|
||||||
|
"docker-compose-fixturenet-optimism.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(fixturenet_optimism_compose_file, 'r') as yaml_file:
|
||||||
|
yaml = YAML()
|
||||||
|
yaml_data = yaml.load(yaml_file)
|
||||||
|
|
||||||
|
# mount the funding script to volumes
|
||||||
|
fund_accounts_script = (
|
||||||
|
'../config/watcher-mobymask-v3-demo/local/fund-accounts-on-l2.sh'
|
||||||
|
':/app/packages/contracts-bedrock/fund-accounts-on-l2.sh'
|
||||||
|
)
|
||||||
|
yaml_data['services']['fixturenet-optimism-contracts']['volumes'].append(fund_accounts_script)
|
||||||
|
|
||||||
|
# update command to run the script
|
||||||
|
yaml_data['services']['fixturenet-optimism-contracts']['command'] = (
|
||||||
|
'"./wait-for-it.sh -h ${CERC_L1_HOST:-$${DEFAULT_CERC_L1_HOST}}'
|
||||||
|
' -p ${CERC_L1_PORT:-$${DEFAULT_CERC_L1_PORT}} -s -t 60'
|
||||||
|
' -- ./deploy-contracts.sh && ./fund-accounts-on-l2.sh"'
|
||||||
|
)
|
||||||
|
|
||||||
|
# update port mapping for op-geth: 0.0.0.0:8545:8545
|
||||||
|
existing_ports = yaml_data['services']['op-geth']['ports']
|
||||||
|
new_ports = ["0.0.0.0:8545:8545" if "8545" in s else s for s in existing_ports]
|
||||||
|
yaml_data['services']['op-geth']['ports'] = new_ports
|
||||||
|
|
||||||
|
with open(fixturenet_optimism_compose_file, 'w') as yaml_file:
|
||||||
|
yaml = YAML()
|
||||||
|
yaml.dump(yaml_data, yaml_file)
|
||||||
|
|
||||||
|
return None
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
version: "1.0"
|
||||||
|
description: "MobyMask v3 demo stack"
|
||||||
|
name: mobymask-v3-demo
|
||||||
|
repos:
|
||||||
|
# fixturenet repos
|
||||||
|
- git.vdb.to/cerc-io/go-ethereum@v1.11.6-statediff-v5
|
||||||
|
- git.vdb.to/cerc-io/lighthouse
|
||||||
|
- github.com/dboreham/foundry
|
||||||
|
- github.com/ethereum-optimism/optimism@op-node/v1.3.0
|
||||||
|
- github.com/ethereum-optimism/op-geth@v1.101304.0
|
||||||
|
# nitro repo
|
||||||
|
- github.com/cerc-io/ts-nitro@v0.1.15
|
||||||
|
# mobymask watcher repos
|
||||||
|
- github.com/cerc-io/mobymask-v2-watcher-ts@v0.2.4
|
||||||
|
- github.com/cerc-io/MobyMask@v0.1.3
|
||||||
|
# mobymask app repos
|
||||||
|
- github.com/cerc-io/mobymask-snap
|
||||||
|
- github.com/cerc-io/mobymask-ui@v0.2.2
|
||||||
|
containers:
|
||||||
|
# fixturenet images
|
||||||
|
- cerc/go-ethereum
|
||||||
|
- cerc/lighthouse
|
||||||
|
- cerc/lighthouse-cli
|
||||||
|
- cerc/fixturenet-eth-genesis
|
||||||
|
- cerc/fixturenet-eth-geth
|
||||||
|
- cerc/fixturenet-eth-lighthouse
|
||||||
|
- cerc/foundry
|
||||||
|
- cerc/optimism-contracts
|
||||||
|
- cerc/optimism-op-node
|
||||||
|
- cerc/optimism-l2geth
|
||||||
|
- cerc/optimism-op-batcher
|
||||||
|
- cerc/optimism-op-proposer
|
||||||
|
# nitro contract deployment image
|
||||||
|
- cerc/nitro-contracts
|
||||||
|
# mobymask watcher images
|
||||||
|
- cerc/watcher-mobymask-v3
|
||||||
|
- cerc/mobymask
|
||||||
|
# mobymask app images
|
||||||
|
- cerc/mobymask-snap
|
||||||
|
- cerc/mobymask-ui
|
||||||
|
pods:
|
||||||
|
- fixturenet-eth
|
||||||
|
- fixturenet-optimism
|
||||||
|
- nitro-contracts
|
||||||
|
- watcher-mobymask-v3-demo
|
||||||
|
- mobymask-snap
|
||||||
|
- mobymask-app-v3-demo
|
||||||
@@ -75,7 +75,7 @@ Create and update an env file to be used in the next step ([defaults](../../conf
|
|||||||
# Uses a generated peer id if not set (disables consensus)
|
# Uses a generated peer id if not set (disables consensus)
|
||||||
CERC_PEER_ID=
|
CERC_PEER_ID=
|
||||||
|
|
||||||
# Disable payments to upstream ETH server
|
# Enable payments to upstream ETH server
|
||||||
CERC_ENABLE_UPSTREAM_PAYMENTS=false
|
CERC_ENABLE_UPSTREAM_PAYMENTS=false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ The MobyMask watcher is a Laconic Network component that provides efficient acce
|
|||||||
|
|
||||||
## Deploy the MobyMask Watcher
|
## Deploy the MobyMask Watcher
|
||||||
|
|
||||||
The instructions below show how to deploy a MobyMask watcher using laconic-stack-orchestrator (the installation of which is covered [here](https://github.com/cerc-io/stack-orchestrator#install)).
|
The instructions below show how to deploy a MobyMask watcher using laconic-stack-orchestrator (the installation of which is covered [here](https://git.vdb.to/cerc-io/stack-orchestrator#install)).
|
||||||
|
|
||||||
This deployment expects that ipld-eth-server's endpoints are available on the local machine at http://ipld-eth-server.example.com:8083/graphql and http://ipld-eth-server.example.com:8082. More advanced configurations are supported by modifying the watcher's [config file](../../config/watcher-mobymask/mobymask-watcher.toml).
|
This deployment expects that ipld-eth-server's endpoints are available on the local machine at http://ipld-eth-server.example.com:8083/graphql and http://ipld-eth-server.example.com:8082. More advanced configurations are supported by modifying the watcher's [config file](../../config/watcher-mobymask/mobymask-watcher.toml).
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ Place the pre-configured watcher alerts rules in Grafana provisioning directory:
|
|||||||
|
|
||||||
Update the alerting contact points config (`monitoring-watchers-deployment/config/monitoring/grafana/provisioning/alerting/contactpoints.yml`) with desired contact points
|
Update the alerting contact points config (`monitoring-watchers-deployment/config/monitoring/grafana/provisioning/alerting/contactpoints.yml`) with desired contact points
|
||||||
|
|
||||||
Add corresponding routes to the notification policies config (`monitoring-watchers-deployment/monitoring/grafana/provisioning/alerting/policies.yaml`) with appropriate object-matchers:
|
Add corresponding routes to the notification policies config (`monitoring-watchers-deployment/config/monitoring/grafana/provisioning/alerting/policies.yml`) with appropriate object-matchers:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# ping-pub
|
||||||
|
Experimental block explorer for laconic
|
||||||
|
|
||||||
|
```
|
||||||
|
laconic-so --stack ping-pub setup-repositories
|
||||||
|
laconic-so --stack ping-pub build-containers
|
||||||
|
laconic-so --stack ping-pub deploy init --output ping-pub-spec.yml --map-ports-to-host localhost-same
|
||||||
|
laconic-so --stack ping-pub deploy create --spec-file ping-pub-spec.yml --deployment-dir pp-deployment
|
||||||
|
laconic-so deployment --dir pp-deployment start
|
||||||
|
```
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
version: "0.1"
|
||||||
|
name: ping-pub
|
||||||
|
repos:
|
||||||
|
# fork, but only for config & Dockerfile reasonsb
|
||||||
|
- github.com/LaconicNetwork/explorer@laconic
|
||||||
|
containers:
|
||||||
|
- cerc/ping-pub
|
||||||
|
pods:
|
||||||
|
- ping-pub
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
version: "1.0"
|
||||||
|
name: snowballtools-base-backend
|
||||||
|
description: "snowballtools-base-backend"
|
||||||
|
repos:
|
||||||
|
- github.com/snowball-tools/snowballtools-base
|
||||||
|
containers:
|
||||||
|
- cerc/webapp-base
|
||||||
|
- cerc/snowballtools-base-backend
|
||||||
|
pods:
|
||||||
|
- snowballtools-base-backend
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Test Database Stack
|
||||||
|
|
||||||
|
A stack with a database for test/demo purposes.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
version: "1.0"
|
||||||
|
name: test
|
||||||
|
description: "A test database stack"
|
||||||
|
repos:
|
||||||
|
containers:
|
||||||
|
- cerc/test-database-container
|
||||||
|
- cerc/test-database-client
|
||||||
|
pods:
|
||||||
|
- test-database
|
||||||
@@ -2,10 +2,10 @@ version: "1.0"
|
|||||||
name: webapp-deployer-backend
|
name: webapp-deployer-backend
|
||||||
description: "Deployer for webapps"
|
description: "Deployer for webapps"
|
||||||
repos:
|
repos:
|
||||||
- git.vdb.to:telackey/webapp-deployment-status-api
|
- git.vdb.to/telackey/webapp-deployment-status-api
|
||||||
containers:
|
containers:
|
||||||
- cerc/webapp-deployer-backend
|
- cerc/webapp-deployer-backend
|
||||||
pods:
|
pods:
|
||||||
- name: webapp-deployer-backend
|
- name: webapp-deployer-backend
|
||||||
repository: git.vdb.to:telackey/webapp-deployment-status-api
|
repository: git.vdb.to/telackey/webapp-deployment-status-api
|
||||||
path: ./
|
path: ./
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from stack_orchestrator.opts import opts
|
|||||||
from stack_orchestrator.util import (get_stack_file_path, get_parsed_deployment_spec, get_parsed_stack_config,
|
from stack_orchestrator.util import (get_stack_file_path, get_parsed_deployment_spec, get_parsed_stack_config,
|
||||||
global_options, get_yaml, get_pod_list, get_pod_file_path, pod_has_scripts,
|
global_options, get_yaml, get_pod_list, get_pod_file_path, pod_has_scripts,
|
||||||
get_pod_script_paths, get_plugin_code_paths, error_exit, env_var_map_from_file)
|
get_pod_script_paths, get_plugin_code_paths, error_exit, env_var_map_from_file)
|
||||||
|
from stack_orchestrator.deploy.spec import Spec
|
||||||
from stack_orchestrator.deploy.deploy_types import LaconicStackSetupCommand
|
from stack_orchestrator.deploy.deploy_types import LaconicStackSetupCommand
|
||||||
from stack_orchestrator.deploy.deployer_factory import getDeployerConfigGenerator
|
from stack_orchestrator.deploy.deployer_factory import getDeployerConfigGenerator
|
||||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||||
@@ -111,6 +112,7 @@ def _create_bind_dir_if_relative(volume, path_string, compose_dir):
|
|||||||
|
|
||||||
# See: https://stackoverflow.com/questions/45699189/editing-docker-compose-yml-with-pyyaml
|
# See: https://stackoverflow.com/questions/45699189/editing-docker-compose-yml-with-pyyaml
|
||||||
def _fixup_pod_file(pod, spec, compose_dir):
|
def _fixup_pod_file(pod, spec, compose_dir):
|
||||||
|
deployment_type = spec[constants.deploy_to_key]
|
||||||
# Fix up volumes
|
# Fix up volumes
|
||||||
if "volumes" in spec:
|
if "volumes" in spec:
|
||||||
spec_volumes = spec["volumes"]
|
spec_volumes = spec["volumes"]
|
||||||
@@ -119,27 +121,34 @@ def _fixup_pod_file(pod, spec, compose_dir):
|
|||||||
for volume in pod_volumes.keys():
|
for volume in pod_volumes.keys():
|
||||||
if volume in spec_volumes:
|
if volume in spec_volumes:
|
||||||
volume_spec = spec_volumes[volume]
|
volume_spec = spec_volumes[volume]
|
||||||
volume_spec_fixedup = volume_spec if Path(volume_spec).is_absolute() else f".{volume_spec}"
|
if volume_spec:
|
||||||
_create_bind_dir_if_relative(volume, volume_spec, compose_dir)
|
volume_spec_fixedup = volume_spec if Path(volume_spec).is_absolute() else f".{volume_spec}"
|
||||||
new_volume_spec = {"driver": "local",
|
_create_bind_dir_if_relative(volume, volume_spec, compose_dir)
|
||||||
"driver_opts": {
|
# this is Docker specific
|
||||||
"type": "none",
|
if spec.is_docker_deployment():
|
||||||
"device": volume_spec_fixedup,
|
new_volume_spec = {
|
||||||
"o": "bind"
|
"driver": "local",
|
||||||
}
|
"driver_opts": {
|
||||||
}
|
"type": "none",
|
||||||
pod["volumes"][volume] = new_volume_spec
|
"device": volume_spec_fixedup,
|
||||||
|
"o": "bind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pod["volumes"][volume] = new_volume_spec
|
||||||
|
|
||||||
# Fix up configmaps
|
# Fix up configmaps
|
||||||
if "configmaps" in spec:
|
if constants.configmaps_key in spec:
|
||||||
spec_cfgmaps = spec["configmaps"]
|
if spec.is_kubernetes_deployment():
|
||||||
if "volumes" in pod:
|
spec_cfgmaps = spec[constants.configmaps_key]
|
||||||
pod_volumes = pod["volumes"]
|
if "volumes" in pod:
|
||||||
for volume in pod_volumes.keys():
|
pod_volumes = pod[constants.volumes_key]
|
||||||
if volume in spec_cfgmaps:
|
for volume in pod_volumes.keys():
|
||||||
volume_cfg = spec_cfgmaps[volume]
|
if volume in spec_cfgmaps:
|
||||||
# Just make the dir (if necessary)
|
volume_cfg = spec_cfgmaps[volume]
|
||||||
_create_bind_dir_if_relative(volume, volume_cfg, compose_dir)
|
# Just make the dir (if necessary)
|
||||||
|
_create_bind_dir_if_relative(volume, volume_cfg, compose_dir)
|
||||||
|
else:
|
||||||
|
print(f"Warning: ConfigMaps not supported for {deployment_type}")
|
||||||
|
|
||||||
# Fix up ports
|
# Fix up ports
|
||||||
if "network" in spec and "ports" in spec["network"]:
|
if "network" in spec and "ports" in spec["network"]:
|
||||||
@@ -323,7 +332,7 @@ def init_operation(deploy_command_context, stack, deployer_type, config,
|
|||||||
if image_registry is None:
|
if image_registry is None:
|
||||||
error_exit("--image-registry must be supplied with --deploy-to k8s")
|
error_exit("--image-registry must be supplied with --deploy-to k8s")
|
||||||
spec_file_content.update({constants.kube_config_key: kube_config})
|
spec_file_content.update({constants.kube_config_key: kube_config})
|
||||||
spec_file_content.update({constants.image_resigtry_key: image_registry})
|
spec_file_content.update({constants.image_registry_key: image_registry})
|
||||||
else:
|
else:
|
||||||
# Check for --kube-config supplied for non-relevant deployer types
|
# Check for --kube-config supplied for non-relevant deployer types
|
||||||
if kube_config is not None:
|
if kube_config is not None:
|
||||||
@@ -358,10 +367,16 @@ def init_operation(deploy_command_context, stack, deployer_type, config,
|
|||||||
volume_descriptors = {}
|
volume_descriptors = {}
|
||||||
configmap_descriptors = {}
|
configmap_descriptors = {}
|
||||||
for named_volume in named_volumes["rw"]:
|
for named_volume in named_volumes["rw"]:
|
||||||
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
if "k8s" in deployer_type:
|
||||||
|
volume_descriptors[named_volume] = None
|
||||||
|
else:
|
||||||
|
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
for named_volume in named_volumes["ro"]:
|
for named_volume in named_volumes["ro"]:
|
||||||
if "k8s" in deployer_type and "config" in named_volume:
|
if "k8s" in deployer_type:
|
||||||
configmap_descriptors[named_volume] = f"./data/{named_volume}"
|
if "config" in named_volume:
|
||||||
|
configmap_descriptors[named_volume] = f"./configmaps/{named_volume}"
|
||||||
|
else:
|
||||||
|
volume_descriptors[named_volume] = None
|
||||||
else:
|
else:
|
||||||
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
volume_descriptors[named_volume] = f"./data/{named_volume}"
|
||||||
if volume_descriptors:
|
if volume_descriptors:
|
||||||
@@ -406,6 +421,17 @@ def _create_deployment_file(deployment_dir: Path):
|
|||||||
output_file.write(f"{constants.cluster_id_key}: {cluster}\n")
|
output_file.write(f"{constants.cluster_id_key}: {cluster}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def _check_volume_definitions(spec):
|
||||||
|
if spec.is_kubernetes_deployment():
|
||||||
|
for volume_name, volume_path in spec.get_volumes().items():
|
||||||
|
if volume_path:
|
||||||
|
if not os.path.isabs(volume_path):
|
||||||
|
raise Exception(
|
||||||
|
f"Relative path {volume_path} for volume {volume_name} not "
|
||||||
|
f"supported for deployment type {spec.get_deployment_type()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("--spec-file", required=True, help="Spec file to use to create this deployment")
|
@click.option("--spec-file", required=True, help="Spec file to use to create this deployment")
|
||||||
@click.option("--deployment-dir", help="Create deployment files in this directory")
|
@click.option("--deployment-dir", help="Create deployment files in this directory")
|
||||||
@@ -421,7 +447,8 @@ def create(ctx, spec_file, deployment_dir, network_dir, initial_peers):
|
|||||||
# The init command's implementation is in a separate function so that we can
|
# The init command's implementation is in a separate function so that we can
|
||||||
# call it from other commands, bypassing the click decoration stuff
|
# call it from other commands, bypassing the click decoration stuff
|
||||||
def create_operation(deployment_command_context, spec_file, deployment_dir, network_dir, initial_peers):
|
def create_operation(deployment_command_context, spec_file, deployment_dir, network_dir, initial_peers):
|
||||||
parsed_spec = get_parsed_deployment_spec(spec_file)
|
parsed_spec = Spec(os.path.abspath(spec_file), get_parsed_deployment_spec(spec_file))
|
||||||
|
_check_volume_definitions(parsed_spec)
|
||||||
stack_name = parsed_spec["stack"]
|
stack_name = parsed_spec["stack"]
|
||||||
deployment_type = parsed_spec[constants.deploy_to_key]
|
deployment_type = parsed_spec[constants.deploy_to_key]
|
||||||
stack_file = get_stack_file_path(stack_name)
|
stack_file = get_stack_file_path(stack_name)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ def push_images_operation(command_context: DeployCommandContext, deployment_cont
|
|||||||
cluster_context = command_context.cluster_context
|
cluster_context = command_context.cluster_context
|
||||||
images: Set[str] = images_for_deployment(cluster_context.compose_files)
|
images: Set[str] = images_for_deployment(cluster_context.compose_files)
|
||||||
# Tag the images for the remote repo
|
# Tag the images for the remote repo
|
||||||
remote_repo_url = deployment_context.spec.obj[constants.image_resigtry_key]
|
remote_repo_url = deployment_context.spec.obj[constants.image_registry_key]
|
||||||
docker = DockerClient()
|
docker = DockerClient()
|
||||||
for image in images:
|
for image in images:
|
||||||
if _image_needs_pushed(image):
|
if _image_needs_pushed(image):
|
||||||
|
|||||||
@@ -21,13 +21,42 @@ from typing import Any, List, Set
|
|||||||
from stack_orchestrator.opts import opts
|
from stack_orchestrator.opts import opts
|
||||||
from stack_orchestrator.util import env_var_map_from_file
|
from stack_orchestrator.util import env_var_map_from_file
|
||||||
from stack_orchestrator.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files
|
from stack_orchestrator.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files
|
||||||
from stack_orchestrator.deploy.k8s.helpers import get_node_pv_mount_path
|
from stack_orchestrator.deploy.k8s.helpers import get_kind_pv_bind_mount_path
|
||||||
from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map
|
from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map, envs_from_compose_file, merge_envs
|
||||||
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment
|
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment
|
||||||
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
||||||
from stack_orchestrator.deploy.spec import Spec
|
from stack_orchestrator.deploy.spec import Spec, Resources, ResourceLimits
|
||||||
from stack_orchestrator.deploy.images import remote_tag_for_image
|
from stack_orchestrator.deploy.images import remote_tag_for_image
|
||||||
|
|
||||||
|
DEFAULT_VOLUME_RESOURCES = Resources({
|
||||||
|
"reservations": {"storage": "2Gi"}
|
||||||
|
})
|
||||||
|
|
||||||
|
DEFAULT_CONTAINER_RESOURCES = Resources({
|
||||||
|
"reservations": {"cpus": "0.1", "memory": "200M"},
|
||||||
|
"limits": {"cpus": "1.0", "memory": "2000M"},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_k8s_resource_requirements(resources: Resources) -> client.V1ResourceRequirements:
|
||||||
|
def to_dict(limits: ResourceLimits):
|
||||||
|
if not limits:
|
||||||
|
return None
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
if limits.cpus:
|
||||||
|
ret["cpu"] = str(limits.cpus)
|
||||||
|
if limits.memory:
|
||||||
|
ret["memory"] = f"{int(limits.memory / (1000 * 1000))}M"
|
||||||
|
if limits.storage:
|
||||||
|
ret["storage"] = f"{int(limits.storage / (1000 * 1000))}M"
|
||||||
|
return ret
|
||||||
|
|
||||||
|
return client.V1ResourceRequirements(
|
||||||
|
requests=to_dict(resources.reservations),
|
||||||
|
limits=to_dict(resources.limits)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterInfo:
|
class ClusterInfo:
|
||||||
parsed_pod_yaml_map: Any
|
parsed_pod_yaml_map: Any
|
||||||
@@ -49,7 +78,30 @@ class ClusterInfo:
|
|||||||
if (opts.o.debug):
|
if (opts.o.debug):
|
||||||
print(f"Env vars: {self.environment_variables.map}")
|
print(f"Env vars: {self.environment_variables.map}")
|
||||||
|
|
||||||
def get_ingress(self):
|
def get_nodeport(self):
|
||||||
|
for pod_name in self.parsed_pod_yaml_map:
|
||||||
|
pod = self.parsed_pod_yaml_map[pod_name]
|
||||||
|
services = pod["services"]
|
||||||
|
for service_name in services:
|
||||||
|
service_info = services[service_name]
|
||||||
|
if "ports" in service_info:
|
||||||
|
port = int(service_info["ports"][0])
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"service port: {port}")
|
||||||
|
service = client.V1Service(
|
||||||
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-nodeport"),
|
||||||
|
spec=client.V1ServiceSpec(
|
||||||
|
type="NodePort",
|
||||||
|
ports=[client.V1ServicePort(
|
||||||
|
port=port,
|
||||||
|
target_port=port
|
||||||
|
)],
|
||||||
|
selector={"app": self.app_name}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return service
|
||||||
|
|
||||||
|
def get_ingress(self, use_tls=False):
|
||||||
# No ingress for a deployment that has no http-proxy defined, for now
|
# No ingress for a deployment that has no http-proxy defined, for now
|
||||||
http_proxy_info_list = self.spec.get_http_proxy()
|
http_proxy_info_list = self.spec.get_http_proxy()
|
||||||
ingress = None
|
ingress = None
|
||||||
@@ -64,7 +116,7 @@ class ClusterInfo:
|
|||||||
tls = [client.V1IngressTLS(
|
tls = [client.V1IngressTLS(
|
||||||
hosts=[host_name],
|
hosts=[host_name],
|
||||||
secret_name=f"{self.app_name}-tls"
|
secret_name=f"{self.app_name}-tls"
|
||||||
)]
|
)] if use_tls else None
|
||||||
paths = []
|
paths = []
|
||||||
for route in http_proxy_info["routes"]:
|
for route in http_proxy_info["routes"]:
|
||||||
path = route["path"]
|
path = route["path"]
|
||||||
@@ -135,26 +187,40 @@ class ClusterInfo:
|
|||||||
result = []
|
result = []
|
||||||
spec_volumes = self.spec.get_volumes()
|
spec_volumes = self.spec.get_volumes()
|
||||||
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
||||||
|
resources = self.spec.get_volume_resources()
|
||||||
|
if not resources:
|
||||||
|
resources = DEFAULT_VOLUME_RESOURCES
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Spec Volumes: {spec_volumes}")
|
print(f"Spec Volumes: {spec_volumes}")
|
||||||
print(f"Named Volumes: {named_volumes}")
|
print(f"Named Volumes: {named_volumes}")
|
||||||
for volume_name in spec_volumes:
|
print(f"Resources: {resources}")
|
||||||
|
for volume_name, volume_path in spec_volumes.items():
|
||||||
if volume_name not in named_volumes:
|
if volume_name not in named_volumes:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"{volume_name} not in pod files")
|
print(f"{volume_name} not in pod files")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
"app": self.app_name,
|
||||||
|
"volume-label": f"{self.app_name}-{volume_name}"
|
||||||
|
}
|
||||||
|
if volume_path:
|
||||||
|
storage_class_name = "manual"
|
||||||
|
k8s_volume_name = f"{self.app_name}-{volume_name}"
|
||||||
|
else:
|
||||||
|
# These will be auto-assigned.
|
||||||
|
storage_class_name = None
|
||||||
|
k8s_volume_name = None
|
||||||
|
|
||||||
spec = client.V1PersistentVolumeClaimSpec(
|
spec = client.V1PersistentVolumeClaimSpec(
|
||||||
access_modes=["ReadWriteOnce"],
|
access_modes=["ReadWriteOnce"],
|
||||||
storage_class_name="manual",
|
storage_class_name=storage_class_name,
|
||||||
resources=client.V1ResourceRequirements(
|
resources=to_k8s_resource_requirements(resources),
|
||||||
requests={"storage": "2Gi"}
|
volume_name=k8s_volume_name
|
||||||
),
|
|
||||||
volume_name=f"{self.app_name}-{volume_name}"
|
|
||||||
)
|
)
|
||||||
pvc = client.V1PersistentVolumeClaim(
|
pvc = client.V1PersistentVolumeClaim(
|
||||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}", labels=labels),
|
||||||
labels={"volume-label": f"{self.app_name}-{volume_name}"}),
|
spec=spec
|
||||||
spec=spec,
|
|
||||||
)
|
)
|
||||||
result.append(pvc)
|
result.append(pvc)
|
||||||
return result
|
return result
|
||||||
@@ -192,16 +258,35 @@ class ClusterInfo:
|
|||||||
result = []
|
result = []
|
||||||
spec_volumes = self.spec.get_volumes()
|
spec_volumes = self.spec.get_volumes()
|
||||||
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map)
|
||||||
for volume_name in spec_volumes:
|
resources = self.spec.get_volume_resources()
|
||||||
|
if not resources:
|
||||||
|
resources = DEFAULT_VOLUME_RESOURCES
|
||||||
|
for volume_name, volume_path in spec_volumes.items():
|
||||||
|
# We only need to create a volume if it is fully qualified HostPath.
|
||||||
|
# Otherwise, we create the PVC and expect the node to allocate the volume for us.
|
||||||
|
if not volume_path:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"{volume_name} does not require an explicit PersistentVolume, since it is not a bind-mount.")
|
||||||
|
continue
|
||||||
|
|
||||||
if volume_name not in named_volumes:
|
if volume_name not in named_volumes:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"{volume_name} not in pod files")
|
print(f"{volume_name} not in pod files")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not os.path.isabs(volume_path):
|
||||||
|
print(f"WARNING: {volume_name}:{volume_path} is not absolute, cannot bind volume.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.spec.is_kind_deployment():
|
||||||
|
host_path = client.V1HostPathVolumeSource(path=get_kind_pv_bind_mount_path(volume_name))
|
||||||
|
else:
|
||||||
|
host_path = client.V1HostPathVolumeSource(path=volume_path)
|
||||||
spec = client.V1PersistentVolumeSpec(
|
spec = client.V1PersistentVolumeSpec(
|
||||||
storage_class_name="manual",
|
storage_class_name="manual",
|
||||||
access_modes=["ReadWriteOnce"],
|
access_modes=["ReadWriteOnce"],
|
||||||
capacity={"storage": "2Gi"},
|
capacity=to_k8s_resource_requirements(resources).requests,
|
||||||
host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name))
|
host_path=host_path
|
||||||
)
|
)
|
||||||
pv = client.V1PersistentVolume(
|
pv = client.V1PersistentVolume(
|
||||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}",
|
||||||
@@ -214,6 +299,9 @@ class ClusterInfo:
|
|||||||
# TODO: put things like image pull policy into an object-scope struct
|
# TODO: put things like image pull policy into an object-scope struct
|
||||||
def get_deployment(self, image_pull_policy: str = None):
|
def get_deployment(self, image_pull_policy: str = None):
|
||||||
containers = []
|
containers = []
|
||||||
|
resources = self.spec.get_container_resources()
|
||||||
|
if not resources:
|
||||||
|
resources = DEFAULT_CONTAINER_RESOURCES
|
||||||
for pod_name in self.parsed_pod_yaml_map:
|
for pod_name in self.parsed_pod_yaml_map:
|
||||||
pod = self.parsed_pod_yaml_map[pod_name]
|
pod = self.parsed_pod_yaml_map[pod_name]
|
||||||
services = pod["services"]
|
services = pod["services"]
|
||||||
@@ -226,6 +314,13 @@ class ClusterInfo:
|
|||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"image: {image}")
|
print(f"image: {image}")
|
||||||
print(f"service port: {port}")
|
print(f"service port: {port}")
|
||||||
|
merged_envs = merge_envs(
|
||||||
|
envs_from_compose_file(
|
||||||
|
service_info["environment"]), self.environment_variables.map
|
||||||
|
) if "environment" in service_info else self.environment_variables.map
|
||||||
|
envs = envs_from_environment_variables_map(merged_envs)
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Merged envs: {envs}")
|
||||||
# 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
|
||||||
@@ -234,19 +329,40 @@ class ClusterInfo:
|
|||||||
name=container_name,
|
name=container_name,
|
||||||
image=image_to_use,
|
image=image_to_use,
|
||||||
image_pull_policy=image_pull_policy,
|
image_pull_policy=image_pull_policy,
|
||||||
env=envs_from_environment_variables_map(self.environment_variables.map),
|
env=envs,
|
||||||
ports=[client.V1ContainerPort(container_port=port)],
|
ports=[client.V1ContainerPort(container_port=port)],
|
||||||
volume_mounts=volume_mounts,
|
volume_mounts=volume_mounts,
|
||||||
resources=client.V1ResourceRequirements(
|
security_context=client.V1SecurityContext(
|
||||||
requests={"cpu": "100m", "memory": "200Mi"},
|
privileged=self.spec.get_privileged(),
|
||||||
limits={"cpu": "1000m", "memory": "2000Mi"},
|
capabilities=client.V1Capabilities(
|
||||||
|
add=self.spec.get_capabilities()
|
||||||
|
) if self.spec.get_capabilities() else None
|
||||||
),
|
),
|
||||||
|
resources=to_k8s_resource_requirements(resources),
|
||||||
)
|
)
|
||||||
containers.append(container)
|
containers.append(container)
|
||||||
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name)
|
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name)
|
||||||
image_pull_secrets = [client.V1LocalObjectReference(name="laconic-registry")]
|
image_pull_secrets = [client.V1LocalObjectReference(name="laconic-registry")]
|
||||||
|
|
||||||
|
annotations = None
|
||||||
|
labels = {"app": self.app_name}
|
||||||
|
|
||||||
|
if self.spec.get_annotations():
|
||||||
|
annotations = {}
|
||||||
|
for key, value in self.spec.get_annotations().items():
|
||||||
|
for service_name in services:
|
||||||
|
annotations[key.replace("{name}", service_name)] = value
|
||||||
|
|
||||||
|
if self.spec.get_labels():
|
||||||
|
for key, value in self.spec.get_labels().items():
|
||||||
|
for service_name in services:
|
||||||
|
labels[key.replace("{name}", service_name)] = value
|
||||||
|
|
||||||
template = client.V1PodTemplateSpec(
|
template = client.V1PodTemplateSpec(
|
||||||
metadata=client.V1ObjectMeta(labels={"app": self.app_name}),
|
metadata=client.V1ObjectMeta(
|
||||||
|
annotations=annotations,
|
||||||
|
labels=labels
|
||||||
|
),
|
||||||
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
|
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
|
||||||
)
|
)
|
||||||
spec = client.V1DeploymentSpec(
|
spec = client.V1DeploymentSpec(
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ from kubernetes import client, config
|
|||||||
from stack_orchestrator import constants
|
from stack_orchestrator import constants
|
||||||
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
|
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
|
||||||
from stack_orchestrator.deploy.k8s.helpers import create_cluster, destroy_cluster, load_images_into_kind
|
from stack_orchestrator.deploy.k8s.helpers import create_cluster, destroy_cluster, load_images_into_kind
|
||||||
from stack_orchestrator.deploy.k8s.helpers import pods_in_deployment, log_stream_from_string, generate_kind_config
|
from stack_orchestrator.deploy.k8s.helpers import install_ingress_for_kind, wait_for_ingress_in_kind
|
||||||
|
from stack_orchestrator.deploy.k8s.helpers import pods_in_deployment, containers_in_pod, log_stream_from_string
|
||||||
|
from stack_orchestrator.deploy.k8s.helpers import generate_kind_config
|
||||||
from stack_orchestrator.deploy.k8s.cluster_info import ClusterInfo
|
from stack_orchestrator.deploy.k8s.cluster_info import ClusterInfo
|
||||||
from stack_orchestrator.opts import opts
|
from stack_orchestrator.opts import opts
|
||||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||||
@@ -88,6 +90,16 @@ class K8sDeployer(Deployer):
|
|||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this pv: {pv}")
|
print(f"Sending this pv: {pv}")
|
||||||
if not opts.o.dry_run:
|
if not opts.o.dry_run:
|
||||||
|
try:
|
||||||
|
pv_resp = self.core_api.read_persistent_volume(name=pv.metadata.name)
|
||||||
|
if pv_resp:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVs already present:")
|
||||||
|
print(f"{pv_resp}")
|
||||||
|
continue
|
||||||
|
except: # noqa: E722
|
||||||
|
pass
|
||||||
|
|
||||||
pv_resp = self.core_api.create_persistent_volume(body=pv)
|
pv_resp = self.core_api.create_persistent_volume(body=pv)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("PVs created:")
|
print("PVs created:")
|
||||||
@@ -100,6 +112,17 @@ class K8sDeployer(Deployer):
|
|||||||
print(f"Sending this pvc: {pvc}")
|
print(f"Sending this pvc: {pvc}")
|
||||||
|
|
||||||
if not opts.o.dry_run:
|
if not opts.o.dry_run:
|
||||||
|
try:
|
||||||
|
pvc_resp = self.core_api.read_namespaced_persistent_volume_claim(
|
||||||
|
name=pvc.metadata.name, namespace=self.k8s_namespace)
|
||||||
|
if pvc_resp:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVCs already present:")
|
||||||
|
print(f"{pvc_resp}")
|
||||||
|
continue
|
||||||
|
except: # noqa: E722
|
||||||
|
pass
|
||||||
|
|
||||||
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
pvc_resp = self.core_api.create_namespaced_persistent_volume_claim(body=pvc, namespace=self.k8s_namespace)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("PVCs created:")
|
print("PVCs created:")
|
||||||
@@ -154,60 +177,80 @@ class K8sDeployer(Deployer):
|
|||||||
# Ensure the referenced containers are copied into kind
|
# Ensure the referenced containers are copied into kind
|
||||||
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
||||||
self.connect_api()
|
self.connect_api()
|
||||||
|
if self.is_kind():
|
||||||
|
# Now configure an ingress controller (not installed by default in kind)
|
||||||
|
install_ingress_for_kind()
|
||||||
|
# Wait for ingress to start (deployment provisioning will fail unless this is done)
|
||||||
|
wait_for_ingress_in_kind()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("Dry run mode enabled, skipping k8s API connect")
|
print("Dry run mode enabled, skipping k8s API connect")
|
||||||
|
|
||||||
self._create_volume_data()
|
self._create_volume_data()
|
||||||
self._create_deployment()
|
self._create_deployment()
|
||||||
|
|
||||||
if not self.is_kind():
|
# Note: at present we don't support tls for kind (and enabling tls causes errors)
|
||||||
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=not self.is_kind())
|
||||||
|
if ingress:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Sending this ingress: {ingress}")
|
||||||
|
if not opts.o.dry_run:
|
||||||
|
ingress_resp = self.networking_api.create_namespaced_ingress(
|
||||||
|
namespace=self.k8s_namespace,
|
||||||
|
body=ingress
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("Ingress created:")
|
||||||
|
print(f"{ingress_resp}")
|
||||||
|
else:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("No ingress configured")
|
||||||
|
|
||||||
if ingress:
|
nodeport: client.V1Service = self.cluster_info.get_nodeport()
|
||||||
|
if nodeport:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Sending this nodeport: {nodeport}")
|
||||||
|
if not opts.o.dry_run:
|
||||||
|
nodeport_resp = self.core_api.create_namespaced_service(
|
||||||
|
namespace=self.k8s_namespace,
|
||||||
|
body=nodeport
|
||||||
|
)
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Sending this ingress: {ingress}")
|
print("NodePort created:")
|
||||||
if not opts.o.dry_run:
|
print(f"{nodeport_resp}")
|
||||||
ingress_resp = self.networking_api.create_namespaced_ingress(
|
|
||||||
namespace=self.k8s_namespace,
|
|
||||||
body=ingress
|
|
||||||
)
|
|
||||||
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): # noqa: C901
|
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
|
|
||||||
pvs = self.cluster_info.get_pvs()
|
|
||||||
for pv in pvs:
|
|
||||||
if opts.o.debug:
|
|
||||||
print(f"Deleting this pv: {pv}")
|
|
||||||
try:
|
|
||||||
pv_resp = self.core_api.delete_persistent_volume(name=pv.metadata.name)
|
|
||||||
if opts.o.debug:
|
|
||||||
print("PV deleted:")
|
|
||||||
print(f"{pv_resp}")
|
|
||||||
except client.exceptions.ApiException as e:
|
|
||||||
_check_delete_exception(e)
|
|
||||||
|
|
||||||
# Figure out the PVCs for this deployment
|
if volumes:
|
||||||
pvcs = self.cluster_info.get_pvcs()
|
# Create the host-path-mounted PVs for this deployment
|
||||||
for pvc in pvcs:
|
pvs = self.cluster_info.get_pvs()
|
||||||
if opts.o.debug:
|
for pv in pvs:
|
||||||
print(f"Deleting this pvc: {pvc}")
|
|
||||||
try:
|
|
||||||
pvc_resp = self.core_api.delete_namespaced_persistent_volume_claim(
|
|
||||||
name=pvc.metadata.name, namespace=self.k8s_namespace
|
|
||||||
)
|
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print("PVCs deleted:")
|
print(f"Deleting this pv: {pv}")
|
||||||
print(f"{pvc_resp}")
|
try:
|
||||||
except client.exceptions.ApiException as e:
|
pv_resp = self.core_api.delete_persistent_volume(name=pv.metadata.name)
|
||||||
_check_delete_exception(e)
|
if opts.o.debug:
|
||||||
|
print("PV deleted:")
|
||||||
|
print(f"{pv_resp}")
|
||||||
|
except client.exceptions.ApiException as e:
|
||||||
|
_check_delete_exception(e)
|
||||||
|
|
||||||
|
# Figure out the PVCs for this deployment
|
||||||
|
pvcs = self.cluster_info.get_pvcs()
|
||||||
|
for pvc in pvcs:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Deleting this pvc: {pvc}")
|
||||||
|
try:
|
||||||
|
pvc_resp = self.core_api.delete_namespaced_persistent_volume_claim(
|
||||||
|
name=pvc.metadata.name, namespace=self.k8s_namespace
|
||||||
|
)
|
||||||
|
if opts.o.debug:
|
||||||
|
print("PVCs deleted:")
|
||||||
|
print(f"{pvc_resp}")
|
||||||
|
except client.exceptions.ApiException as e:
|
||||||
|
_check_delete_exception(e)
|
||||||
|
|
||||||
# Figure out the ConfigMaps for this deployment
|
# Figure out the ConfigMaps for this deployment
|
||||||
cfg_maps = self.cluster_info.get_configmaps()
|
cfg_maps = self.cluster_info.get_configmaps()
|
||||||
@@ -245,20 +288,34 @@ class K8sDeployer(Deployer):
|
|||||||
except client.exceptions.ApiException as e:
|
except client.exceptions.ApiException as e:
|
||||||
_check_delete_exception(e)
|
_check_delete_exception(e)
|
||||||
|
|
||||||
if not self.is_kind():
|
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=not self.is_kind())
|
||||||
ingress: client.V1Ingress = self.cluster_info.get_ingress()
|
if ingress:
|
||||||
if ingress:
|
if opts.o.debug:
|
||||||
if opts.o.debug:
|
print(f"Deleting this ingress: {ingress}")
|
||||||
print(f"Deleting this ingress: {ingress}")
|
try:
|
||||||
try:
|
self.networking_api.delete_namespaced_ingress(
|
||||||
self.networking_api.delete_namespaced_ingress(
|
name=ingress.metadata.name, namespace=self.k8s_namespace
|
||||||
name=ingress.metadata.name, namespace=self.k8s_namespace
|
)
|
||||||
)
|
except client.exceptions.ApiException as e:
|
||||||
except client.exceptions.ApiException as e:
|
_check_delete_exception(e)
|
||||||
_check_delete_exception(e)
|
else:
|
||||||
else:
|
if opts.o.debug:
|
||||||
if opts.o.debug:
|
print("No ingress to delete")
|
||||||
print("No ingress to delete")
|
|
||||||
|
nodeport: client.V1Service = self.cluster_info.get_nodeport()
|
||||||
|
if nodeport:
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"Deleting this nodeport: {ingress}")
|
||||||
|
try:
|
||||||
|
self.core_api.delete_namespaced_service(
|
||||||
|
namespace=self.k8s_namespace,
|
||||||
|
name=nodeport.metadata.name
|
||||||
|
)
|
||||||
|
except client.exceptions.ApiException as e:
|
||||||
|
_check_delete_exception(e)
|
||||||
|
else:
|
||||||
|
if opts.o.debug:
|
||||||
|
print("No nodeport to delete")
|
||||||
|
|
||||||
if self.is_kind():
|
if self.is_kind():
|
||||||
# Destroy the kind cluster
|
# Destroy the kind cluster
|
||||||
@@ -359,9 +416,15 @@ class K8sDeployer(Deployer):
|
|||||||
log_data = "******* Pods not running ********\n"
|
log_data = "******* Pods not running ********\n"
|
||||||
else:
|
else:
|
||||||
k8s_pod_name = pods[0]
|
k8s_pod_name = pods[0]
|
||||||
|
containers = containers_in_pod(self.core_api, k8s_pod_name)
|
||||||
# If the pod is not yet started, the logs request below will throw an exception
|
# If the pod is not yet started, the logs request below will throw an exception
|
||||||
try:
|
try:
|
||||||
log_data = self.core_api.read_namespaced_pod_log(k8s_pod_name, namespace="default", container="test")
|
log_data = ""
|
||||||
|
for container in containers:
|
||||||
|
container_log = self.core_api.read_namespaced_pod_log(k8s_pod_name, namespace="default", container=container)
|
||||||
|
container_log_lines = container_log.splitlines()
|
||||||
|
for line in container_log_lines:
|
||||||
|
log_data += f"{container}: {line}\n"
|
||||||
except client.exceptions.ApiException as e:
|
except client.exceptions.ApiException as e:
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"Error from read_namespaced_pod_log: {e}")
|
print(f"Error from read_namespaced_pod_log: {e}")
|
||||||
|
|||||||
@@ -13,12 +13,14 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from kubernetes import client
|
from kubernetes import client, utils, watch
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import re
|
||||||
from typing import Set, Mapping, List
|
from typing import Set, Mapping, List
|
||||||
|
|
||||||
|
from stack_orchestrator.util import get_k8s_dir, error_exit
|
||||||
from stack_orchestrator.opts import opts
|
from stack_orchestrator.opts import opts
|
||||||
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names
|
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names
|
||||||
from stack_orchestrator.deploy.deployer import DeployerException
|
from stack_orchestrator.deploy.deployer import DeployerException
|
||||||
@@ -43,6 +45,33 @@ def destroy_cluster(name: str):
|
|||||||
_run_command(f"kind delete cluster --name {name}")
|
_run_command(f"kind delete cluster --name {name}")
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_ingress_in_kind():
|
||||||
|
core_v1 = client.CoreV1Api()
|
||||||
|
for i in range(20):
|
||||||
|
warned_waiting = False
|
||||||
|
w = watch.Watch()
|
||||||
|
for event in w.stream(func=core_v1.list_namespaced_pod,
|
||||||
|
namespace="ingress-nginx",
|
||||||
|
label_selector="app.kubernetes.io/component=controller",
|
||||||
|
timeout_seconds=30):
|
||||||
|
if event['object'].status.container_statuses:
|
||||||
|
if event['object'].status.container_statuses[0].ready is True:
|
||||||
|
if warned_waiting:
|
||||||
|
print("Ingress controller is ready")
|
||||||
|
return
|
||||||
|
print("Waiting for ingress controller to become ready...")
|
||||||
|
warned_waiting = True
|
||||||
|
error_exit("ERROR: Timed out waiting for ingress to become ready")
|
||||||
|
|
||||||
|
|
||||||
|
def install_ingress_for_kind():
|
||||||
|
api_client = client.ApiClient()
|
||||||
|
ingress_install = os.path.abspath(get_k8s_dir().joinpath("components", "ingress", "ingress-nginx-kind-deploy.yaml"))
|
||||||
|
if opts.o.debug:
|
||||||
|
print("Installing nginx ingress controller in kind cluster")
|
||||||
|
utils.create_from_yaml(api_client, yaml_file=ingress_install)
|
||||||
|
|
||||||
|
|
||||||
def load_images_into_kind(kind_cluster_name: str, image_set: Set[str]):
|
def load_images_into_kind(kind_cluster_name: str, image_set: Set[str]):
|
||||||
for image in image_set:
|
for image in image_set:
|
||||||
result = _run_command(f"kind load docker-image {image} --name {kind_cluster_name}")
|
result = _run_command(f"kind load docker-image {image} --name {kind_cluster_name}")
|
||||||
@@ -61,6 +90,17 @@ def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str):
|
|||||||
return pods
|
return pods
|
||||||
|
|
||||||
|
|
||||||
|
def containers_in_pod(core_api: client.CoreV1Api, pod_name: str):
|
||||||
|
containers = []
|
||||||
|
pod_response = core_api.read_namespaced_pod(pod_name, namespace="default")
|
||||||
|
if opts.o.debug:
|
||||||
|
print(f"pod_response: {pod_response}")
|
||||||
|
pod_containers = pod_response.spec.containers
|
||||||
|
for pod_container in pod_containers:
|
||||||
|
containers.append(pod_container.name)
|
||||||
|
return containers
|
||||||
|
|
||||||
|
|
||||||
def log_stream_from_string(s: str):
|
def log_stream_from_string(s: str):
|
||||||
# Note response has to be UTF-8 encoded because the caller expects to decode it
|
# Note response has to be UTF-8 encoded because the caller expects to decode it
|
||||||
yield ("ignore", s.encode())
|
yield ("ignore", s.encode())
|
||||||
@@ -80,7 +120,7 @@ def named_volumes_from_pod_files(parsed_pod_files):
|
|||||||
return named_volumes
|
return named_volumes
|
||||||
|
|
||||||
|
|
||||||
def get_node_pv_mount_path(volume_name: str):
|
def get_kind_pv_bind_mount_path(volume_name: str):
|
||||||
return f"/mnt/{volume_name}"
|
return f"/mnt/{volume_name}"
|
||||||
|
|
||||||
|
|
||||||
@@ -105,11 +145,14 @@ def volume_mounts_for_service(parsed_pod_files, service):
|
|||||||
mount_path = mount_split[1]
|
mount_path = mount_split[1]
|
||||||
mount_options = mount_split[2] if len(mount_split) == 3 else None
|
mount_options = mount_split[2] if len(mount_split) == 3 else None
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"volumne_name: {volume_name}")
|
print(f"volume_name: {volume_name}")
|
||||||
print(f"mount path: {mount_path}")
|
print(f"mount path: {mount_path}")
|
||||||
print(f"mount options: {mount_options}")
|
print(f"mount options: {mount_options}")
|
||||||
volume_device = client.V1VolumeMount(
|
volume_device = client.V1VolumeMount(
|
||||||
mount_path=mount_path, name=volume_name, read_only="ro" == mount_options)
|
mount_path=mount_path,
|
||||||
|
name=volume_name,
|
||||||
|
read_only="ro" == mount_options
|
||||||
|
)
|
||||||
result.append(volume_device)
|
result.append(volume_device)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -132,18 +175,8 @@ def volumes_for_pod_files(parsed_pod_files, spec, app_name):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_host_paths_for_volumes(parsed_pod_files):
|
def _get_host_paths_for_volumes(deployment_context):
|
||||||
result = {}
|
return deployment_context.spec.get_volumes()
|
||||||
for pod in parsed_pod_files:
|
|
||||||
parsed_pod_file = parsed_pod_files[pod]
|
|
||||||
if "volumes" in parsed_pod_file:
|
|
||||||
volumes = parsed_pod_file["volumes"]
|
|
||||||
for volume_name in volumes.keys():
|
|
||||||
volume_definition = volumes[volume_name]
|
|
||||||
if volume_definition and "driver_opts" in volume_definition:
|
|
||||||
host_path = volume_definition["driver_opts"]["device"]
|
|
||||||
result[volume_name] = host_path
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Path:
|
def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Path:
|
||||||
@@ -151,12 +184,12 @@ def _make_absolute_host_path(data_mount_path: Path, deployment_dir: Path) -> Pat
|
|||||||
return data_mount_path
|
return data_mount_path
|
||||||
else:
|
else:
|
||||||
# Python Path voodo that looks pretty odd:
|
# Python Path voodo that looks pretty odd:
|
||||||
return Path.cwd().joinpath(deployment_dir.joinpath("compose").joinpath(data_mount_path)).resolve()
|
return Path.cwd().joinpath(deployment_dir.joinpath(data_mount_path)).resolve()
|
||||||
|
|
||||||
|
|
||||||
def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
||||||
volume_definitions = []
|
volume_definitions = []
|
||||||
volume_host_path_map = _get_host_paths_for_volumes(parsed_pod_files)
|
volume_host_path_map = _get_host_paths_for_volumes(deployment_context)
|
||||||
# Note these paths are relative to the location of the pod files (at present)
|
# Note these paths are relative to the location of the pod files (at present)
|
||||||
# So we need to fix up to make them correct and absolute because kind assumes
|
# So we need to fix up to make them correct and absolute because kind assumes
|
||||||
# relative to the cwd.
|
# relative to the cwd.
|
||||||
@@ -176,14 +209,15 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
|||||||
volume_name = mount_split[0]
|
volume_name = mount_split[0]
|
||||||
mount_path = mount_split[1]
|
mount_path = mount_split[1]
|
||||||
if opts.o.debug:
|
if opts.o.debug:
|
||||||
print(f"volumne_name: {volume_name}")
|
print(f"volume_name: {volume_name}")
|
||||||
print(f"map: {volume_host_path_map}")
|
print(f"map: {volume_host_path_map}")
|
||||||
print(f"mount path: {mount_path}")
|
print(f"mount path: {mount_path}")
|
||||||
if volume_name not in deployment_context.spec.get_configmaps():
|
if volume_name not in deployment_context.spec.get_configmaps():
|
||||||
volume_definitions.append(
|
if volume_host_path_map[volume_name]:
|
||||||
f" - hostPath: {_make_absolute_host_path(volume_host_path_map[volume_name], deployment_dir)}\n"
|
volume_definitions.append(
|
||||||
f" containerPath: {get_node_pv_mount_path(volume_name)}\n"
|
f" - hostPath: {_make_absolute_host_path(volume_host_path_map[volume_name], deployment_dir)}\n"
|
||||||
)
|
f" containerPath: {get_kind_pv_bind_mount_path(volume_name)}\n"
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
"" if len(volume_definitions) == 0 else (
|
"" if len(volume_definitions) == 0 else (
|
||||||
" extraMounts:\n"
|
" extraMounts:\n"
|
||||||
@@ -192,7 +226,8 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _generate_kind_port_mappings(parsed_pod_files):
|
# TODO: decide if we need this functionality
|
||||||
|
def _generate_kind_port_mappings_from_services(parsed_pod_files):
|
||||||
port_definitions = []
|
port_definitions = []
|
||||||
for pod in parsed_pod_files:
|
for pod in parsed_pod_files:
|
||||||
parsed_pod_file = parsed_pod_files[pod]
|
parsed_pod_file = parsed_pod_files[pod]
|
||||||
@@ -214,6 +249,46 @@ def _generate_kind_port_mappings(parsed_pod_files):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_kind_port_mappings(parsed_pod_files):
|
||||||
|
port_definitions = []
|
||||||
|
# For now we just map port 80 for the nginx ingress controller we install in kind
|
||||||
|
port_string = "80"
|
||||||
|
port_definitions.append(f" - containerPort: {port_string}\n hostPort: {port_string}\n")
|
||||||
|
return (
|
||||||
|
"" if len(port_definitions) == 0 else (
|
||||||
|
" extraPortMappings:\n"
|
||||||
|
f"{''.join(port_definitions)}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Note: this makes any duplicate definition in b overwrite a
|
||||||
|
def merge_envs(a: Mapping[str, str], b: Mapping[str, str]) -> Mapping[str, str]:
|
||||||
|
result = {**a, **b}
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _expand_shell_vars(raw_val: str) -> str:
|
||||||
|
# could be: <string> or ${<env-var-name>} or ${<env-var-name>:-<default-value>}
|
||||||
|
# TODO: implement support for variable substitution and default values
|
||||||
|
# if raw_val is like ${<something>} print a warning and substitute an empty string
|
||||||
|
# otherwise return raw_val
|
||||||
|
match = re.search(r"^\$\{(.*)\}$", raw_val)
|
||||||
|
if match:
|
||||||
|
print(f"WARNING: found unimplemented environment variable substitution: {raw_val}")
|
||||||
|
else:
|
||||||
|
return raw_val
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: handle the case where the same env var is defined in multiple places
|
||||||
|
def envs_from_compose_file(compose_file_envs: Mapping[str, str]) -> Mapping[str, str]:
|
||||||
|
result = {}
|
||||||
|
for env_var, env_val in compose_file_envs.items():
|
||||||
|
expanded_env_val = _expand_shell_vars(env_val)
|
||||||
|
result.update({env_var: expanded_env_val})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def envs_from_environment_variables_map(map: Mapping[str, str]) -> List[client.V1EnvVar]:
|
def envs_from_environment_variables_map(map: Mapping[str, str]) -> List[client.V1EnvVar]:
|
||||||
result = []
|
result = []
|
||||||
for env_var, env_val in map.items():
|
for env_var, env_val in map.items():
|
||||||
@@ -251,6 +326,12 @@ def generate_kind_config(deployment_dir: Path, deployment_context):
|
|||||||
"apiVersion: kind.x-k8s.io/v1alpha4\n"
|
"apiVersion: kind.x-k8s.io/v1alpha4\n"
|
||||||
"nodes:\n"
|
"nodes:\n"
|
||||||
"- role: control-plane\n"
|
"- role: control-plane\n"
|
||||||
|
" kubeadmConfigPatches:\n"
|
||||||
|
" - |\n"
|
||||||
|
" kind: InitConfiguration\n"
|
||||||
|
" nodeRegistration:\n"
|
||||||
|
" kubeletExtraArgs:\n"
|
||||||
|
" node-labels: \"ingress-ready=true\"\n"
|
||||||
f"{port_mappings_yml}\n"
|
f"{port_mappings_yml}\n"
|
||||||
f"{mounts_yml}\n"
|
f"{mounts_yml}\n"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,19 +13,77 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
import typing
|
import typing
|
||||||
|
import humanfriendly
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from stack_orchestrator.util import get_yaml
|
from stack_orchestrator.util import get_yaml
|
||||||
from stack_orchestrator import constants
|
from stack_orchestrator import constants
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceLimits:
|
||||||
|
cpus: float = None
|
||||||
|
memory: int = None
|
||||||
|
storage: int = None
|
||||||
|
|
||||||
|
def __init__(self, obj={}):
|
||||||
|
if "cpus" in obj:
|
||||||
|
self.cpus = float(obj["cpus"])
|
||||||
|
if "memory" in obj:
|
||||||
|
self.memory = humanfriendly.parse_size(obj["memory"])
|
||||||
|
if "storage" in obj:
|
||||||
|
self.storage = humanfriendly.parse_size(obj["storage"])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.__dict__)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for k in self.__dict__:
|
||||||
|
yield k, self.__dict__[k]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self.__dict__)
|
||||||
|
|
||||||
|
|
||||||
|
class Resources:
|
||||||
|
limits: ResourceLimits = None
|
||||||
|
reservations: ResourceLimits = None
|
||||||
|
|
||||||
|
def __init__(self, obj={}):
|
||||||
|
if "reservations" in obj:
|
||||||
|
self.reservations = ResourceLimits(obj["reservations"])
|
||||||
|
if "limits" in obj:
|
||||||
|
self.limits = ResourceLimits(obj["limits"])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.__dict__)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for k in self.__dict__:
|
||||||
|
yield k, self.__dict__[k]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self.__dict__)
|
||||||
|
|
||||||
|
|
||||||
class Spec:
|
class Spec:
|
||||||
|
|
||||||
obj: typing.Any
|
obj: typing.Any
|
||||||
file_path: Path
|
file_path: Path
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, file_path: Path = None, obj={}) -> None:
|
||||||
pass
|
self.file_path = file_path
|
||||||
|
self.obj = obj
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.obj[item]
|
||||||
|
|
||||||
|
def __contains__(self, item):
|
||||||
|
return item in self.obj
|
||||||
|
|
||||||
|
def get(self, item, default=None):
|
||||||
|
return self.obj.get(item, default)
|
||||||
|
|
||||||
def init_from_file(self, file_path: Path):
|
def init_from_file(self, file_path: Path):
|
||||||
with file_path:
|
with file_path:
|
||||||
@@ -33,8 +91,8 @@ class Spec:
|
|||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
|
||||||
def get_image_registry(self):
|
def get_image_registry(self):
|
||||||
return (self.obj[constants.image_resigtry_key]
|
return (self.obj[constants.image_registry_key]
|
||||||
if self.obj and constants.image_resigtry_key in self.obj
|
if self.obj and constants.image_registry_key in self.obj
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
def get_volumes(self):
|
def get_volumes(self):
|
||||||
@@ -47,8 +105,38 @@ class Spec:
|
|||||||
if self.obj and "configmaps" in self.obj
|
if self.obj and "configmaps" in self.obj
|
||||||
else {})
|
else {})
|
||||||
|
|
||||||
|
def get_container_resources(self):
|
||||||
|
return Resources(self.obj.get("resources", {}).get("containers", {}))
|
||||||
|
|
||||||
|
def get_volume_resources(self):
|
||||||
|
return Resources(self.obj.get("resources", {}).get("volumes", {}))
|
||||||
|
|
||||||
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
|
||||||
and constants.http_proxy_key in self.obj[constants.network_key]
|
and constants.http_proxy_key in self.obj[constants.network_key]
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
|
def get_annotations(self):
|
||||||
|
return self.obj.get("annotations", {})
|
||||||
|
|
||||||
|
def get_labels(self):
|
||||||
|
return self.obj.get("labels", {})
|
||||||
|
|
||||||
|
def get_privileged(self):
|
||||||
|
return "true" == str(self.obj.get("security", {}).get("privileged", "false")).lower()
|
||||||
|
|
||||||
|
def get_capabilities(self):
|
||||||
|
return self.obj.get("security", {}).get("capabilities", [])
|
||||||
|
|
||||||
|
def get_deployment_type(self):
|
||||||
|
return self.obj[constants.deploy_to_key]
|
||||||
|
|
||||||
|
def is_kubernetes_deployment(self):
|
||||||
|
return self.get_deployment_type() in [constants.k8s_kind_deploy_type, constants.k8s_deploy_type]
|
||||||
|
|
||||||
|
def is_kind_deployment(self):
|
||||||
|
return self.get_deployment_type() in [constants.k8s_kind_deploy_type]
|
||||||
|
|
||||||
|
def is_docker_deployment(self):
|
||||||
|
return self.get_deployment_type() in [constants.compose_deploy_type]
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def _fixup_url_spec(spec_file_name: str, url: str):
|
|||||||
- host-name: {parsed_url.hostname}
|
- host-name: {parsed_url.hostname}
|
||||||
routes:
|
routes:
|
||||||
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
- path: '{parsed_url.path if parsed_url.path else "/"}'
|
||||||
proxy-to: webapp:3000
|
proxy-to: webapp:80
|
||||||
'''
|
'''
|
||||||
spec_file_path = Path(spec_file_name)
|
spec_file_path = Path(spec_file_name)
|
||||||
with open(spec_file_path) as rfile:
|
with open(spec_file_path) as rfile:
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import shlex
|
|||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ from stack_orchestrator.deploy.webapp.util import (LaconicRegistryClient,
|
|||||||
build_container_image, push_container_image,
|
build_container_image, push_container_image,
|
||||||
file_hash, deploy_to_k8s, publish_deployment,
|
file_hash, deploy_to_k8s, publish_deployment,
|
||||||
hostname_for_deployment_request, generate_hostname_for_app,
|
hostname_for_deployment_request, generate_hostname_for_app,
|
||||||
match_owner)
|
match_owner, skip_by_tag)
|
||||||
|
|
||||||
|
|
||||||
def process_app_deployment_request(
|
def process_app_deployment_request(
|
||||||
@@ -39,8 +41,19 @@ def process_app_deployment_request(
|
|||||||
dns_suffix,
|
dns_suffix,
|
||||||
deployment_parent_dir,
|
deployment_parent_dir,
|
||||||
kube_config,
|
kube_config,
|
||||||
image_registry
|
image_registry,
|
||||||
|
log_parent_dir
|
||||||
):
|
):
|
||||||
|
run_id = f"{app_deployment_request.id}-{str(time.time()).split('.')[0]}-{str(uuid.uuid4()).split('-')[0]}"
|
||||||
|
log_file = None
|
||||||
|
if log_parent_dir:
|
||||||
|
log_dir = os.path.join(log_parent_dir, app_deployment_request.id)
|
||||||
|
if not os.path.exists(log_dir):
|
||||||
|
os.mkdir(log_dir)
|
||||||
|
log_file_path = os.path.join(log_dir, f"{run_id}.log")
|
||||||
|
print(f"Directing build logs to: {log_file_path}")
|
||||||
|
log_file = open(log_file_path, "wt")
|
||||||
|
|
||||||
# 1. look up application
|
# 1. look up application
|
||||||
app = laconic.get_record(app_deployment_request.attributes.application, require=True)
|
app = laconic.get_record(app_deployment_request.attributes.application, require=True)
|
||||||
|
|
||||||
@@ -102,8 +115,10 @@ def process_app_deployment_request(
|
|||||||
needs_k8s_deploy = False
|
needs_k8s_deploy = False
|
||||||
# 6. build container (if needed)
|
# 6. build container (if needed)
|
||||||
if not deployment_record or deployment_record.attributes.application != app.id:
|
if not deployment_record or deployment_record.attributes.application != app.id:
|
||||||
build_container_image(app, deployment_container_tag)
|
# TODO: pull from request
|
||||||
push_container_image(deployment_dir)
|
extra_build_args = []
|
||||||
|
build_container_image(app, deployment_container_tag, extra_build_args, log_file)
|
||||||
|
push_container_image(deployment_dir, log_file)
|
||||||
needs_k8s_deploy = True
|
needs_k8s_deploy = True
|
||||||
|
|
||||||
# 7. update config (if needed)
|
# 7. update config (if needed)
|
||||||
@@ -116,6 +131,7 @@ def process_app_deployment_request(
|
|||||||
deploy_to_k8s(
|
deploy_to_k8s(
|
||||||
deployment_record,
|
deployment_record,
|
||||||
deployment_dir,
|
deployment_dir,
|
||||||
|
log_file
|
||||||
)
|
)
|
||||||
|
|
||||||
publish_deployment(
|
publish_deployment(
|
||||||
@@ -162,10 +178,14 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
|||||||
@click.option("--record-namespace-dns", help="eg, crn://laconic/dns")
|
@click.option("--record-namespace-dns", help="eg, crn://laconic/dns")
|
||||||
@click.option("--record-namespace-deployments", help="eg, crn://laconic/deployments")
|
@click.option("--record-namespace-deployments", help="eg, crn://laconic/deployments")
|
||||||
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
||||||
|
@click.option("--include-tags", help="Only include requests with matching tags (comma-separated).", default="")
|
||||||
|
@click.option("--exclude-tags", help="Exclude requests with matching tags (comma-separated).", default="")
|
||||||
|
@click.option("--log-dir", help="Output build/deployment logs to directory.", default=None)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir,
|
def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir, # noqa: C901
|
||||||
request_id, discover, state_file, only_update_state,
|
request_id, discover, state_file, only_update_state,
|
||||||
dns_suffix, record_namespace_dns, record_namespace_deployments, dry_run):
|
dns_suffix, record_namespace_dns, record_namespace_deployments, dry_run,
|
||||||
|
include_tags, exclude_tags, log_dir):
|
||||||
if request_id and discover:
|
if request_id and discover:
|
||||||
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -183,6 +203,10 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
print("--dns-suffix, --record-namespace-dns, and --record-namespace-deployments are all required", file=sys.stderr)
|
print("--dns-suffix, --record-namespace-dns, and --record-namespace-deployments are all required", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
# Split CSV and clean up values.
|
||||||
|
include_tags = [tag.strip() for tag in include_tags.split(",") if tag]
|
||||||
|
exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag]
|
||||||
|
|
||||||
laconic = LaconicRegistryClient(laconic_config)
|
laconic = LaconicRegistryClient(laconic_config)
|
||||||
|
|
||||||
# Find deployment requests.
|
# Find deployment requests.
|
||||||
@@ -204,6 +228,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
requests.sort(key=lambda r: r.createTime)
|
requests.sort(key=lambda r: r.createTime)
|
||||||
requests.reverse()
|
requests.reverse()
|
||||||
requests_by_name = {}
|
requests_by_name = {}
|
||||||
|
skipped_by_name = {}
|
||||||
for r in requests:
|
for r in requests:
|
||||||
# TODO: Do this _after_ filtering deployments and cancellations to minimize round trips.
|
# 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)
|
||||||
@@ -216,17 +241,20 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
requested_name = generate_hostname_for_app(app)
|
requested_name = generate_hostname_for_app(app)
|
||||||
print("Generating name %s for request %s." % (requested_name, r.id))
|
print("Generating name %s for request %s." % (requested_name, r.id))
|
||||||
|
|
||||||
if requested_name not in requests_by_name:
|
if requested_name in skipped_by_name or requested_name in requests_by_name:
|
||||||
print(
|
print("Ignoring request %s, it has been superseded." % r.id)
|
||||||
"Found request %s to run application %s on %s."
|
continue
|
||||||
% (r.id, r.attributes.application, requested_name)
|
|
||||||
)
|
if skip_by_tag(r, include_tags, exclude_tags):
|
||||||
requests_by_name[requested_name] = r
|
print("Skipping request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id,
|
||||||
else:
|
include_tags,
|
||||||
print(
|
exclude_tags,
|
||||||
"Ignoring request %s, it is superseded by %s."
|
r.attributes.tags))
|
||||||
% (r.id, requests_by_name[requested_name].id)
|
skipped_by_name[requested_name] = r
|
||||||
)
|
continue
|
||||||
|
|
||||||
|
print("Found request %s to run application %s on %s." % (r.id, r.attributes.application, requested_name))
|
||||||
|
requests_by_name[requested_name] = r
|
||||||
|
|
||||||
# Find deployments.
|
# Find deployments.
|
||||||
deployments = laconic.app_deployments()
|
deployments = laconic.app_deployments()
|
||||||
@@ -273,7 +301,8 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
|||||||
dns_suffix,
|
dns_suffix,
|
||||||
os.path.abspath(deployment_parent_dir),
|
os.path.abspath(deployment_parent_dir),
|
||||||
kube_config,
|
kube_config,
|
||||||
image_registry
|
image_registry,
|
||||||
|
log_dir
|
||||||
)
|
)
|
||||||
status = "DEPLOYED"
|
status = "DEPLOYED"
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import sys
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from stack_orchestrator.deploy.webapp.util import LaconicRegistryClient, match_owner
|
from stack_orchestrator.deploy.webapp.util import LaconicRegistryClient, match_owner, skip_by_tag
|
||||||
|
|
||||||
|
|
||||||
def process_app_removal_request(ctx,
|
def process_app_removal_request(ctx,
|
||||||
@@ -107,10 +107,12 @@ def dump_known_requests(filename, requests):
|
|||||||
@click.option("--delete-names/--preserve-names", help="Delete all names associated with removed deployments.", default=True)
|
@click.option("--delete-names/--preserve-names", help="Delete all names associated with removed deployments.", default=True)
|
||||||
@click.option("--delete-volumes/--preserve-volumes", default=True, help="delete data volumes")
|
@click.option("--delete-volumes/--preserve-volumes", default=True, help="delete data volumes")
|
||||||
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
||||||
|
@click.option("--include-tags", help="Only include requests with matching tags (comma-separated).", default="")
|
||||||
|
@click.option("--exclude-tags", help="Exclude requests with matching tags (comma-separated).", default="")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def command(ctx, laconic_config, deployment_parent_dir,
|
def command(ctx, laconic_config, deployment_parent_dir,
|
||||||
request_id, discover, state_file, only_update_state,
|
request_id, discover, state_file, only_update_state,
|
||||||
delete_names, delete_volumes, dry_run):
|
delete_names, delete_volumes, dry_run, include_tags, exclude_tags):
|
||||||
if request_id and discover:
|
if request_id and discover:
|
||||||
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -123,6 +125,10 @@ def command(ctx, laconic_config, deployment_parent_dir,
|
|||||||
print("--only-update-state requires --state-file", file=sys.stderr)
|
print("--only-update-state requires --state-file", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
# Split CSV and clean up values.
|
||||||
|
include_tags = [tag.strip() for tag in include_tags.split(",") if tag]
|
||||||
|
exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag]
|
||||||
|
|
||||||
laconic = LaconicRegistryClient(laconic_config)
|
laconic = LaconicRegistryClient(laconic_config)
|
||||||
|
|
||||||
# Find deployment removal requests.
|
# Find deployment removal requests.
|
||||||
@@ -141,6 +147,7 @@ def command(ctx, laconic_config, deployment_parent_dir,
|
|||||||
|
|
||||||
previous_requests = load_known_requests(state_file)
|
previous_requests = load_known_requests(state_file)
|
||||||
requests.sort(key=lambda r: r.createTime)
|
requests.sort(key=lambda r: r.createTime)
|
||||||
|
requests.reverse()
|
||||||
|
|
||||||
# Find deployments.
|
# Find deployments.
|
||||||
deployments = {}
|
deployments = {}
|
||||||
@@ -155,10 +162,22 @@ def command(ctx, laconic_config, deployment_parent_dir,
|
|||||||
# TODO: should we handle CRNs?
|
# TODO: should we handle CRNs?
|
||||||
removals_by_deployment[r.attributes.deployment] = r
|
removals_by_deployment[r.attributes.deployment] = r
|
||||||
|
|
||||||
requests_to_execute = []
|
one_per_deployment = {}
|
||||||
for r in requests:
|
for r in requests:
|
||||||
if not r.attributes.deployment:
|
if not r.attributes.deployment:
|
||||||
print(f"Skipping removal request {r.id} since it was a cancellation.")
|
print(f"Skipping removal request {r.id} since it was a cancellation.")
|
||||||
|
elif r.attributes.deployment in one_per_deployment:
|
||||||
|
print(f"Skipping removal request {r.id} since it was superseded.")
|
||||||
|
else:
|
||||||
|
one_per_deployment[r.attributes.deployment] = r
|
||||||
|
|
||||||
|
requests_to_execute = []
|
||||||
|
for r in one_per_deployment.values():
|
||||||
|
if skip_by_tag(r, include_tags, exclude_tags):
|
||||||
|
print("Skipping removal request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id,
|
||||||
|
include_tags,
|
||||||
|
exclude_tags,
|
||||||
|
r.attributes.tags))
|
||||||
elif r.id in removals_by_request:
|
elif r.id in removals_by_request:
|
||||||
print(f"Found satisfied request for {r.id} at {removals_by_request[r.id].id}")
|
print(f"Found satisfied request for {r.id} at {removals_by_request[r.id].id}")
|
||||||
elif r.attributes.deployment in removals_by_deployment:
|
elif r.attributes.deployment in removals_by_deployment:
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ def determine_base_container(clone_dir, app_type="webapp"):
|
|||||||
return base_container
|
return base_container
|
||||||
|
|
||||||
|
|
||||||
def build_container_image(app_record, tag, extra_build_args=[]):
|
def build_container_image(app_record, tag, extra_build_args=[], log_file=None):
|
||||||
tmpdir = tempfile.mkdtemp()
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -227,10 +227,10 @@ def build_container_image(app_record, tag, extra_build_args=[]):
|
|||||||
git_env = dict(os.environ.copy())
|
git_env = dict(os.environ.copy())
|
||||||
# Never prompt
|
# Never prompt
|
||||||
git_env["GIT_TERMINAL_PROMPT"] = "0"
|
git_env["GIT_TERMINAL_PROMPT"] = "0"
|
||||||
subprocess.check_call(["git", "clone", repo, clone_dir], env=git_env)
|
subprocess.check_call(["git", "clone", repo, clone_dir], env=git_env, stdout=log_file, stderr=log_file)
|
||||||
subprocess.check_call(["git", "checkout", ref], cwd=clone_dir, env=git_env)
|
subprocess.check_call(["git", "checkout", ref], cwd=clone_dir, env=git_env, stdout=log_file, stderr=log_file)
|
||||||
else:
|
else:
|
||||||
result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir])
|
result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir], stdout=log_file, stderr=log_file)
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
|
|
||||||
base_container = determine_base_container(clone_dir, app_record.attributes.app_type)
|
base_container = determine_base_container(clone_dir, app_record.attributes.app_type)
|
||||||
@@ -246,25 +246,27 @@ def build_container_image(app_record, tag, 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))
|
||||||
|
|
||||||
result = subprocess.run(build_command)
|
result = subprocess.run(build_command, stdout=log_file, stderr=log_file)
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
finally:
|
finally:
|
||||||
cmd("rm", "-rf", tmpdir)
|
cmd("rm", "-rf", tmpdir)
|
||||||
|
|
||||||
|
|
||||||
def push_container_image(deployment_dir):
|
def push_container_image(deployment_dir, log_file=None):
|
||||||
print("Pushing image ...")
|
print("Pushing image ...")
|
||||||
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, "push-images"])
|
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, "push-images"],
|
||||||
|
stdout=log_file, stderr=log_file)
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
|
|
||||||
|
|
||||||
def deploy_to_k8s(deploy_record, deployment_dir):
|
def deploy_to_k8s(deploy_record, deployment_dir, log_file=None):
|
||||||
if not deploy_record:
|
if not deploy_record:
|
||||||
command = "up"
|
command = "up"
|
||||||
else:
|
else:
|
||||||
command = "update"
|
command = "update"
|
||||||
|
|
||||||
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command])
|
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command],
|
||||||
|
stdout=log_file, stderr=log_file)
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
|
|
||||||
|
|
||||||
@@ -349,3 +351,17 @@ def generate_hostname_for_app(app):
|
|||||||
else:
|
else:
|
||||||
m.update(app.attributes.repository.encode())
|
m.update(app.attributes.repository.encode())
|
||||||
return "%s-%s" % (last_part, m.hexdigest()[0:10])
|
return "%s-%s" % (last_part, m.hexdigest()[0:10])
|
||||||
|
|
||||||
|
|
||||||
|
def skip_by_tag(r, include_tags, exclude_tags):
|
||||||
|
for tag in exclude_tags:
|
||||||
|
if r.attributes.tags and tag in r.attributes.tags:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if include_tags:
|
||||||
|
for tag in include_tags:
|
||||||
|
if r.attributes.tags and tag in r.attributes.tags:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import importlib.resources
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import yaml
|
import yaml
|
||||||
from stack_orchestrator.constants import stack_file_name
|
from stack_orchestrator.constants import stack_file_name
|
||||||
from stack_orchestrator.util import include_exclude_check, stack_is_external, error_exit
|
from stack_orchestrator.util import include_exclude_check, stack_is_external, error_exit, warn_exit
|
||||||
|
|
||||||
|
|
||||||
class GitProgress(git.RemoteProgress):
|
class GitProgress(git.RemoteProgress):
|
||||||
@@ -249,8 +249,8 @@ def command(ctx, include, exclude, git_ssh, check_only, pull, branches, branches
|
|||||||
error_exit(f"stack {stack} does not exist")
|
error_exit(f"stack {stack} does not exist")
|
||||||
with stack_file_path:
|
with stack_file_path:
|
||||||
stack_config = yaml.safe_load(open(stack_file_path, "r"))
|
stack_config = yaml.safe_load(open(stack_file_path, "r"))
|
||||||
if "repos" not in stack_config:
|
if "repos" not in stack_config or stack_config["repos"] is None:
|
||||||
error_exit(f"stack {stack} does not define any repositories")
|
warn_exit(f"stack {stack} does not define any repositories")
|
||||||
else:
|
else:
|
||||||
repos_in_scope = stack_config["repos"]
|
repos_in_scope = stack_config["repos"]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ def get_config_file_dir():
|
|||||||
return source_config_dir
|
return source_config_dir
|
||||||
|
|
||||||
|
|
||||||
|
def get_k8s_dir():
|
||||||
|
data_dir = Path(__file__).absolute().parent.joinpath("data")
|
||||||
|
source_config_dir = data_dir.joinpath("k8s")
|
||||||
|
return source_config_dir
|
||||||
|
|
||||||
|
|
||||||
def get_parsed_deployment_spec(spec_file):
|
def get_parsed_deployment_spec(spec_file):
|
||||||
spec_file_path = Path(spec_file)
|
spec_file_path = Path(spec_file)
|
||||||
try:
|
try:
|
||||||
@@ -189,5 +195,10 @@ def error_exit(s):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def warn_exit(s):
|
||||||
|
print(f"WARN: {s}")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def env_var_map_from_file(file: Path) -> Mapping[str, str]:
|
def env_var_map_from_file(file: Path) -> Mapping[str, str]:
|
||||||
return dotenv_values(file)
|
return dotenv_values(file)
|
||||||
|
|||||||
Executable
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
# Dump environment variables for debugging
|
||||||
|
echo "Environment variables:"
|
||||||
|
env
|
||||||
|
fi
|
||||||
|
|
||||||
|
stack="container-registry"
|
||||||
|
|
||||||
|
# Helper functions: TODO move into a separate file
|
||||||
|
wait_for_pods_started () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
local ps_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir ps )
|
||||||
|
|
||||||
|
if [[ "$ps_output" == *"Running containers:"* ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for pods to start: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_log_output () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
|
||||||
|
local log_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
|
||||||
|
if [[ ! -z "$log_output" ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for pods log content: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delete_cluster_exit () {
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: eventually this test should be folded into ../deploy/
|
||||||
|
# but keeping it separate for now for convenience
|
||||||
|
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||||
|
# Set a non-default repo dir
|
||||||
|
export CERC_REPO_BASE_DIR=~/stack-orchestrator-test/repo-base-dir
|
||||||
|
echo "Testing this package: $TEST_TARGET_SO"
|
||||||
|
echo "Test version command"
|
||||||
|
reported_version_string=$( $TEST_TARGET_SO version )
|
||||||
|
echo "Version reported is: ${reported_version_string}"
|
||||||
|
echo "Cloning repositories into: $CERC_REPO_BASE_DIR"
|
||||||
|
rm -rf $CERC_REPO_BASE_DIR
|
||||||
|
mkdir -p $CERC_REPO_BASE_DIR
|
||||||
|
$TEST_TARGET_SO --stack ${stack} setup-repositories
|
||||||
|
$TEST_TARGET_SO --stack ${stack} build-containers
|
||||||
|
# Test basic stack-orchestrator deploy to k8s
|
||||||
|
test_deployment_dir=$CERC_REPO_BASE_DIR/${stack}-deployment-dir
|
||||||
|
test_deployment_spec=$CERC_REPO_BASE_DIR/${stack}-deployment-spec.yml
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy --deploy-to k8s-kind init --output $test_deployment_spec --config CERC_TEST_PARAM_1=PASSED
|
||||||
|
# Check the file now exists
|
||||||
|
if [ ! -f "$test_deployment_spec" ]; then
|
||||||
|
echo "deploy init test: spec file not present"
|
||||||
|
echo "deploy init test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy init test: passed"
|
||||||
|
|
||||||
|
# Switch to a full path for bind mount.
|
||||||
|
volume_name="registry-data"
|
||||||
|
sed -i "s|^\(\s*${volume_name}:$\)$|\1 ${test_deployment_dir}/data/${volume_name}|" $test_deployment_spec
|
||||||
|
|
||||||
|
# Add ingress config to the spec file
|
||||||
|
ed $test_deployment_spec <<IngressSpec
|
||||||
|
/network:/
|
||||||
|
a
|
||||||
|
http-proxy:
|
||||||
|
- host-name: localhost
|
||||||
|
routes:
|
||||||
|
- path: /
|
||||||
|
proxy-to: registry:5000
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
IngressSpec
|
||||||
|
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
||||||
|
# Check the deployment dir exists
|
||||||
|
if [ ! -d "$test_deployment_dir" ]; then
|
||||||
|
echo "deploy create test: deployment directory not present"
|
||||||
|
echo "deploy create test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy create test: passed"
|
||||||
|
|
||||||
|
# Note: this isn't strictly necessary, except we end up trying to push the image into
|
||||||
|
# the kind cluster then fails because it can't be found locally
|
||||||
|
docker pull registry:2.8
|
||||||
|
|
||||||
|
# Try to start the deployment
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
|
wait_for_pods_started
|
||||||
|
# Check logs command works
|
||||||
|
wait_for_log_output
|
||||||
|
sleep 1
|
||||||
|
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_3" == *"listening on"* ]]; then
|
||||||
|
echo "deployment logs test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment logs test: FAILED"
|
||||||
|
echo $log_output_3
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that we can use the registry
|
||||||
|
# Note: since this pulls from the DockerCo registry without auth it's possible it'll run into rate limiting issues
|
||||||
|
docker pull hello-world
|
||||||
|
docker tag hello-world localhost:80/hello-world
|
||||||
|
docker push localhost:80/hello-world
|
||||||
|
# Then do a quick check that we actually pushed something there
|
||||||
|
# See: https://stackoverflow.com/questions/31251356/how-to-get-a-list-of-images-on-docker-registry-v2
|
||||||
|
registry_response=$(curl -s -X GET http://localhost:80/v2/_catalog)
|
||||||
|
if [[ "$registry_response" == *"{\"repositories\":[\"hello-world\"]}"* ]]; then
|
||||||
|
echo "registry content test: passed"
|
||||||
|
else
|
||||||
|
echo "registry content test: FAILED"
|
||||||
|
echo $registry_response
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop and clean up
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
echo "Test passed"
|
||||||
Executable
+131
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||||
|
set -x
|
||||||
|
# Dump environment variables for debugging
|
||||||
|
echo "Environment variables:"
|
||||||
|
env
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" == "from-path" ]; then
|
||||||
|
TEST_TARGET_SO="laconic-so"
|
||||||
|
else
|
||||||
|
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||||
|
fi
|
||||||
|
|
||||||
|
stack="test-database"
|
||||||
|
spec_file=${stack}-spec.yml
|
||||||
|
deployment_dir=${stack}-deployment
|
||||||
|
|
||||||
|
# Helper functions: TODO move into a separate file
|
||||||
|
wait_for_pods_started () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
local ps_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir ps )
|
||||||
|
|
||||||
|
if [[ "$ps_output" == *"Running containers:"* ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for pods to start: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_test_complete () {
|
||||||
|
for i in {1..50}
|
||||||
|
do
|
||||||
|
|
||||||
|
local log_output=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
|
||||||
|
if [[ "${log_output}" == *"Database test client: test complete"* ]]; then
|
||||||
|
# if ready, return
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# if not ready, wait
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Timed out, error exit
|
||||||
|
echo "waiting for test complete: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delete_cluster_exit () {
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set a non-default repo dir
|
||||||
|
export CERC_REPO_BASE_DIR=~/stack-orchestrator-test/repo-base-dir
|
||||||
|
echo "Testing this package: $TEST_TARGET_SO"
|
||||||
|
echo "Test version command"
|
||||||
|
reported_version_string=$( $TEST_TARGET_SO version )
|
||||||
|
echo "Version reported is: ${reported_version_string}"
|
||||||
|
echo "Cloning repositories into: $CERC_REPO_BASE_DIR"
|
||||||
|
rm -rf $CERC_REPO_BASE_DIR
|
||||||
|
mkdir -p $CERC_REPO_BASE_DIR
|
||||||
|
$TEST_TARGET_SO --stack ${stack} setup-repositories
|
||||||
|
$TEST_TARGET_SO --stack ${stack} build-containers
|
||||||
|
# Test basic stack-orchestrator deploy to k8s
|
||||||
|
test_deployment_dir=$CERC_REPO_BASE_DIR/${deployment_dir}
|
||||||
|
test_deployment_spec=$CERC_REPO_BASE_DIR/${spec_file}
|
||||||
|
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy --deploy-to k8s-kind init --output $test_deployment_spec
|
||||||
|
# Check the file now exists
|
||||||
|
if [ ! -f "$test_deployment_spec" ]; then
|
||||||
|
echo "deploy init test: spec file not present"
|
||||||
|
echo "deploy init test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy init test: passed"
|
||||||
|
|
||||||
|
# Switch to a full path for the data dir so it gets provisioned as a host bind mounted volume and preserved beyond cluster lifetime
|
||||||
|
sed -i "s|^\(\s*db-data:$\)$|\1 ${test_deployment_dir}/data/db-data|" $test_deployment_spec
|
||||||
|
|
||||||
|
$TEST_TARGET_SO --stack ${stack} deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
||||||
|
# Check the deployment dir exists
|
||||||
|
if [ ! -d "$test_deployment_dir" ]; then
|
||||||
|
echo "deploy create test: deployment directory not present"
|
||||||
|
echo "deploy create test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "deploy create test: passed"
|
||||||
|
|
||||||
|
# Try to start the deployment
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
|
wait_for_pods_started
|
||||||
|
# Check logs command works
|
||||||
|
wait_for_test_complete
|
||||||
|
log_output_1=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_1" == *"Database test client: test data does not exist"* ]]; then
|
||||||
|
echo "Create database content test: passed"
|
||||||
|
else
|
||||||
|
echo "Create database content test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop then start again and check the volume was preserved
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
||||||
|
# Sleep a bit just in case
|
||||||
|
sleep 20
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
|
wait_for_pods_started
|
||||||
|
wait_for_test_complete
|
||||||
|
|
||||||
|
log_output_2=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_2" == *"Database test client: test data already exists"* ]]; then
|
||||||
|
echo "Retain database content test: passed"
|
||||||
|
else
|
||||||
|
echo "Retain database content test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop and clean up
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
echo "Test passed"
|
||||||
@@ -6,6 +6,12 @@ fi
|
|||||||
# Dump environment variables for debugging
|
# Dump environment variables for debugging
|
||||||
echo "Environment variables:"
|
echo "Environment variables:"
|
||||||
env
|
env
|
||||||
|
|
||||||
|
delete_cluster_exit () {
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
# Test basic stack-orchestrator deploy
|
# Test basic stack-orchestrator deploy
|
||||||
echo "Running stack-orchestrator deploy test"
|
echo "Running stack-orchestrator deploy test"
|
||||||
# Bit of a hack, test the most recent package
|
# Bit of a hack, test the most recent package
|
||||||
@@ -57,7 +63,7 @@ $TEST_TARGET_SO --stack test deploy down
|
|||||||
# The next time we bring the container up the volume will be old (from the previous run above)
|
# The next time we bring the container up the volume will be old (from the previous run above)
|
||||||
$TEST_TARGET_SO --stack test deploy up
|
$TEST_TARGET_SO --stack test deploy up
|
||||||
log_output_1=$( $TEST_TARGET_SO --stack test deploy logs )
|
log_output_1=$( $TEST_TARGET_SO --stack test deploy logs )
|
||||||
if [[ "$log_output_1" == *"Filesystem is old"* ]]; then
|
if [[ "$log_output_1" == *"filesystem is old"* ]]; then
|
||||||
echo "Retain volumes test: passed"
|
echo "Retain volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Retain volumes test: FAILED"
|
echo "Retain volumes test: FAILED"
|
||||||
@@ -67,7 +73,7 @@ $TEST_TARGET_SO --stack test deploy down --delete-volumes
|
|||||||
# Now when we bring the container up the volume will be new again
|
# Now when we bring the container up the volume will be new again
|
||||||
$TEST_TARGET_SO --stack test deploy up
|
$TEST_TARGET_SO --stack test deploy up
|
||||||
log_output_2=$( $TEST_TARGET_SO --stack test deploy logs )
|
log_output_2=$( $TEST_TARGET_SO --stack test deploy logs )
|
||||||
if [[ "$log_output_2" == *"Filesystem is fresh"* ]]; then
|
if [[ "$log_output_2" == *"filesystem is fresh"* ]]; then
|
||||||
echo "Delete volumes test: passed"
|
echo "Delete volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Delete volumes test: FAILED"
|
echo "Delete volumes test: FAILED"
|
||||||
@@ -106,12 +112,16 @@ 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
|
||||||
# Check logs command works
|
# Check logs command works
|
||||||
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
if [[ "$log_output_3" == *"Filesystem is fresh"* ]]; then
|
if [[ "$log_output_3" == *"filesystem is fresh"* ]]; then
|
||||||
echo "deployment logs test: passed"
|
echo "deployment logs test: passed"
|
||||||
else
|
else
|
||||||
echo "deployment logs test: FAILED"
|
echo "deployment logs test: FAILED"
|
||||||
@@ -124,6 +134,37 @@ else
|
|||||||
echo "deployment config test: FAILED"
|
echo "deployment config test: FAILED"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
# Check the config variable CERC_TEST_PARAM_2 was passed correctly from the compose file
|
||||||
|
if [[ "$log_output_3" == *"Test-param-2: CERC_TEST_PARAM_2_VALUE"* ]]; then
|
||||||
|
echo "deployment compose config test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment compose config test: FAILED"
|
||||||
|
exit 1
|
||||||
|
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
|
||||||
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
||||||
|
# Sleep a bit just in case
|
||||||
|
# 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
|
||||||
|
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_5" == *"filesystem is old"* ]]; then
|
||||||
|
echo "Retain volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "Retain volumes test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Stop and clean up
|
# Stop and clean up
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
echo "Test passed"
|
echo "Test passed"
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ if [ ! -f "$test_deployment_spec" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "deploy init test: passed"
|
echo "deploy init test: passed"
|
||||||
|
|
||||||
|
# Switch to a full path for bind mount.
|
||||||
|
sed -i "s|^\(\s*test-data-bind:$\)$|\1 ${test_deployment_dir}/data/test-data-bind|" $test_deployment_spec
|
||||||
|
|
||||||
$TEST_TARGET_SO --stack test deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
$TEST_TARGET_SO --stack test deploy create --spec-file $test_deployment_spec --deployment-dir $test_deployment_dir
|
||||||
# Check the deployment dir exists
|
# Check the deployment dir exists
|
||||||
if [ ! -d "$test_deployment_dir" ]; then
|
if [ ! -d "$test_deployment_dir" ]; then
|
||||||
@@ -99,7 +103,7 @@ if [ ! "$create_file_content" == "create-command-output-data" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Add a config file to be picked up by the ConfigMap before starting.
|
# Add a config file to be picked up by the ConfigMap before starting.
|
||||||
echo "dbfc7a4d-44a7-416d-b5f3-29842cc47650" > $test_deployment_dir/data/test-config/test_config
|
echo "dbfc7a4d-44a7-416d-b5f3-29842cc47650" > $test_deployment_dir/configmaps/test-config/test_config
|
||||||
|
|
||||||
echo "deploy create output file test: passed"
|
echo "deploy create output file test: passed"
|
||||||
# Try to start the deployment
|
# Try to start the deployment
|
||||||
@@ -107,13 +111,16 @@ $TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
|||||||
wait_for_pods_started
|
wait_for_pods_started
|
||||||
# Check logs command works
|
# Check logs command works
|
||||||
wait_for_log_output
|
wait_for_log_output
|
||||||
|
sleep 1
|
||||||
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
log_output_3=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
if [[ "$log_output_3" == *"Filesystem is fresh"* ]]; then
|
if [[ "$log_output_3" == *"filesystem is fresh"* ]]; then
|
||||||
echo "deployment logs test: passed"
|
echo "deployment logs test: passed"
|
||||||
else
|
else
|
||||||
echo "deployment logs test: FAILED"
|
echo "deployment logs test: FAILED"
|
||||||
|
echo $log_output_3
|
||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check the config variable CERC_TEST_PARAM_1 was passed correctly
|
# Check the config variable CERC_TEST_PARAM_1 was passed correctly
|
||||||
if [[ "$log_output_3" == *"Test-param-1: PASSED"* ]]; then
|
if [[ "$log_output_3" == *"Test-param-1: PASSED"* ]]; then
|
||||||
echo "deployment config test: passed"
|
echo "deployment config test: passed"
|
||||||
@@ -122,6 +129,14 @@ else
|
|||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check the config variable CERC_TEST_PARAM_2 was passed correctly from the compose file
|
||||||
|
if [[ "$log_output_3" == *"Test-param-2: CERC_TEST_PARAM_2_VALUE"* ]]; then
|
||||||
|
echo "deployment compose config test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment compose config test: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Check that the ConfigMap is mounted and contains the expected content.
|
# Check that the ConfigMap is mounted and contains the expected content.
|
||||||
log_output_4=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
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
|
if [[ "$log_output_4" == *"/config/test_config:"* ]] && [[ "$log_output_4" == *"dbfc7a4d-44a7-416d-b5f3-29842cc47650"* ]]; then
|
||||||
@@ -131,6 +146,26 @@ else
|
|||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check that the bind-mount volume is mounted.
|
||||||
|
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_5" == *"/data: MOUNTED"* ]]; then
|
||||||
|
echo "deployment bind volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment bind volumes test: FAILED"
|
||||||
|
echo $log_output_5
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that the provisioner managed volume is mounted.
|
||||||
|
log_output_6=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_6" == *"/data2: MOUNTED"* ]]; then
|
||||||
|
echo "deployment provisioner volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "deployment provisioner volumes test: FAILED"
|
||||||
|
echo $log_output_6
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Stop then start again and check the volume was preserved
|
# Stop then start again and check the volume was preserved
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop
|
||||||
# Sleep a bit just in case
|
# Sleep a bit just in case
|
||||||
@@ -139,13 +174,26 @@ sleep 20
|
|||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir start
|
||||||
wait_for_pods_started
|
wait_for_pods_started
|
||||||
wait_for_log_output
|
wait_for_log_output
|
||||||
log_output_5=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
sleep 1
|
||||||
if [[ "$log_output_5" == *"Filesystem is old"* ]]; then
|
|
||||||
echo "Retain volumes test: passed"
|
log_output_10=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_10" == *"/data filesystem is old"* ]]; then
|
||||||
|
echo "Retain bind volumes test: passed"
|
||||||
else
|
else
|
||||||
echo "Retain volumes test: FAILED"
|
echo "Retain bind volumes test: FAILED"
|
||||||
delete_cluster_exit
|
delete_cluster_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# These volumes will be completely destroyed by the kind delete/create, because they lived inside
|
||||||
|
# the kind container. So, unlike the bind-mount case, they will appear fresh after the restart.
|
||||||
|
log_output_11=$( $TEST_TARGET_SO deployment --dir $test_deployment_dir logs )
|
||||||
|
if [[ "$log_output_11" == *"/data2 filesystem is fresh"* ]]; then
|
||||||
|
echo "Fresh provisioner volumes test: passed"
|
||||||
|
else
|
||||||
|
echo "Fresh provisioner volumes test: FAILED"
|
||||||
|
delete_cluster_exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Stop and clean up
|
# Stop and clean up
|
||||||
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
$TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes
|
||||||
echo "Test passed"
|
echo "Test passed"
|
||||||
|
|||||||
Reference in New Issue
Block a user