forked from cerc-io/stack-orchestrator
Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
952389abb0 | ||
|
|
5c275aa622 | ||
|
|
8576137557 | ||
|
|
65c1cdf6b1 | ||
|
|
265699bc38 | ||
|
|
4a7670a5d6 | ||
|
|
6087e1cd31 | ||
|
|
1def279d26 | ||
|
|
64691bd206 | ||
|
|
aef5986135 | ||
|
|
6f8f0340d3 | ||
|
|
7590d6e237 | ||
|
|
573f99dbbe | ||
|
|
8052c1c25e | ||
|
|
a674d13493 | ||
|
|
0d4f4509c8 | ||
|
|
5af27b1b3a | ||
|
|
6c91b87348 | ||
|
|
7d18334953 | ||
|
|
79c1c5ed99 |
@@ -1,57 +0,0 @@
|
||||
name: Fixturenet-Eth-Plugeth-Arm-Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
paths:
|
||||
- '!**'
|
||||
- '.gitea/workflows/triggers/fixturenet-eth-plugeth-arm-test'
|
||||
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||
- cron: '2 14 * * *'
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "Run an Ethereum plugeth fixturenet test"
|
||||
runs-on: ubuntu-latest-arm
|
||||
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: "Run fixturenet-eth tests"
|
||||
run: ./tests/fixturenet-eth-plugeth/run-test.sh
|
||||
- name: Notify Vulcanize Slack on CI failure
|
||||
if: ${{ always() && github.ref_name == 'main' }}
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.VULCANIZE_SLACK_CI_ALERTS }}
|
||||
- name: Notify DeepStack Slack on CI failure
|
||||
if: ${{ always() && github.ref_name == 'main' }}
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.DEEPSTACK_SLACK_CI_ALERTS }}
|
||||
@@ -1,57 +0,0 @@
|
||||
name: Fixturenet-Eth-Plugeth-Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
paths:
|
||||
- '!**'
|
||||
- '.gitea/workflows/triggers/fixturenet-eth-plugeth-test'
|
||||
schedule: # Note: coordinate with other tests to not overload runners at the same time of day
|
||||
- cron: '2 14 * * *'
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "Run an Ethereum plugeth fixturenet test"
|
||||
runs-on: ubuntu-latest
|
||||
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: "Run fixturenet-eth tests"
|
||||
run: ./tests/fixturenet-eth-plugeth/run-test.sh
|
||||
- name: Notify Vulcanize Slack on CI failure
|
||||
if: ${{ always() && github.ref_name == 'main' }}
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.VULCANIZE_SLACK_CI_ALERTS }}
|
||||
- name: Notify DeepStack Slack on CI failure
|
||||
if: ${{ always() && github.ref_name == 'main' }}
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.DEEPSTACK_SLACK_CI_ALERTS }}
|
||||
@@ -1,55 +0,0 @@
|
||||
name: Fixturenet-Eth-Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
paths:
|
||||
- '!**'
|
||||
- '.gitea/workflows/triggers/fixturenet-eth-test'
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "Run an Ethereum fixturenet test"
|
||||
runs-on: ubuntu-latest
|
||||
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: "Run fixturenet-eth tests"
|
||||
run: ./tests/fixturenet-eth/run-test.sh
|
||||
- name: Notify Vulcanize Slack on CI failure
|
||||
if: ${{ always() && github.ref_name == 'main' }}
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.VULCANIZE_SLACK_CI_ALERTS }}
|
||||
- name: Notify DeepStack Slack on CI failure
|
||||
if: ${{ always() && github.ref_name == 'main' }}
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.DEEPSTACK_SLACK_CI_ALERTS }}
|
||||
@@ -1,2 +0,0 @@
|
||||
Change this file to trigger running the fixturenet-eth-plugeth-arm-test CI job
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Change this file to trigger running the fixturenet-eth-plugeth-test CI job
|
||||
trigger
|
||||
trigger
|
||||
@@ -1,2 +0,0 @@
|
||||
Change this file to trigger running the fixturenet-eth-test CI job
|
||||
|
||||
@@ -7,3 +7,4 @@ Trigger
|
||||
Trigger
|
||||
Trigger
|
||||
Trigger
|
||||
Trigger
|
||||
|
||||
@@ -34,5 +34,6 @@ volumes_key = "volumes"
|
||||
security_key = "security"
|
||||
annotations_key = "annotations"
|
||||
labels_key = "labels"
|
||||
replicas_key = "replicas"
|
||||
kind_config_filename = "kind-config.yml"
|
||||
kube_config_filename = "kubeconfig.yml"
|
||||
|
||||
@@ -10,6 +10,7 @@ MONIKER="localtestnet"
|
||||
KEYRING="test"
|
||||
KEYALGO="secp256k1"
|
||||
LOGLEVEL="${LOGLEVEL:-info}"
|
||||
DENOM="alnt"
|
||||
|
||||
|
||||
if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
|
||||
@@ -33,7 +34,7 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
|
||||
laconicd keys add $KEY --keyring-backend $KEYRING --algo $KEYALGO
|
||||
|
||||
# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer)
|
||||
laconicd init $MONIKER --chain-id $CHAINID --default-denom photon
|
||||
laconicd init $MONIKER --chain-id $CHAINID --default-denom $DENOM
|
||||
|
||||
update_genesis() {
|
||||
jq "$1" $HOME/.laconicd/config/genesis.json > $HOME/.laconicd/config/tmp_genesis.json &&
|
||||
@@ -89,10 +90,12 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
|
||||
fi
|
||||
|
||||
# Allocate genesis accounts (cosmos formatted addresses)
|
||||
laconicd genesis add-genesis-account $KEY 100000000000000000000000000photon --keyring-backend $KEYRING
|
||||
# 10^30 alnt | 10^12 lnt
|
||||
laconicd genesis add-genesis-account $KEY 1000000000000000000000000000000$DENOM --keyring-backend $KEYRING
|
||||
|
||||
# Sign genesis transaction
|
||||
laconicd genesis gentx $KEY 1000000000000000000000photon --keyring-backend $KEYRING --chain-id $CHAINID
|
||||
# 10^24 alnt | 10^6 lnt
|
||||
laconicd genesis gentx $KEY 1000000000000000000000000$DENOM --keyring-backend $KEYRING --chain-id $CHAINID
|
||||
|
||||
# Collect genesis tx
|
||||
laconicd genesis collect-gentxs
|
||||
@@ -107,7 +110,7 @@ fi
|
||||
laconicd start \
|
||||
--pruning=nothing \
|
||||
--log_level $LOGLEVEL \
|
||||
--minimum-gas-prices=0.0001photon \
|
||||
--minimum-gas-prices=1$DENOM \
|
||||
--api.enable \
|
||||
--rpc.laddr="tcp://0.0.0.0:26657" \
|
||||
--gql-server --gql-playground
|
||||
|
||||
@@ -6,4 +6,4 @@ services:
|
||||
bondId:
|
||||
chainId: laconic_9000-1
|
||||
gas: 350000
|
||||
fees: 200000photon
|
||||
fees: 2000000alnt
|
||||
|
||||
@@ -6,4 +6,4 @@ services:
|
||||
bondId:
|
||||
chainId: laconic_9000-1
|
||||
gas: 250000
|
||||
fees: 200000photon
|
||||
fees: 2000000alnt
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
if [[ -n "$CERC_SCRIPT_DEBUG" ]]; then
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
@@ -9,7 +9,7 @@ LOGLEVEL="info"
|
||||
laconicd start \
|
||||
--pruning=nothing \
|
||||
--log_level $LOGLEVEL \
|
||||
--minimum-gas-prices=0.0001photon \
|
||||
--minimum-gas-prices=1alnt \
|
||||
--api.enable \
|
||||
--gql-server \
|
||||
--gql-playground
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ funds_balance=$(echo ${funds_response} | jq -r ".[0].balance[0].quantity")
|
||||
echo "Balance is: ${funds_balance}"
|
||||
|
||||
# Create a bond
|
||||
bond_create_result=$(${registry_command} bond create --type photon --quantity 1000000000)
|
||||
bond_create_result=$(${registry_command} bond create --type alnt --quantity 1000000000)
|
||||
bond_id=$(echo ${bond_create_result} | jq -r .bondId)
|
||||
echo "Created bond with id: ${bond_id}"
|
||||
|
||||
|
||||
+5
-5
@@ -7,16 +7,16 @@
|
||||
"rpc": [
|
||||
{"provider": "LX-tendermint-rpc", "address": "LACONIC_LACONICD_RPC_URL"}
|
||||
],
|
||||
"sdk_version": "0.45.1",
|
||||
"sdk_version": "0.50.3",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "ethm",
|
||||
"addr_prefix": "laconic",
|
||||
"logo": "/logos/cosmos.svg",
|
||||
"assets": [{
|
||||
"base": "photon",
|
||||
"base": "alnt",
|
||||
"symbol": "LNT",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "cosmos",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "cosmos",
|
||||
"logo": "/logos/cosmos.svg"
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -64,5 +64,6 @@ $ laconic-so --stack fixturenet-laconic-loaded deploy exec cli ./scripts/create-
|
||||
Balance is: 99998999999999998999600000
|
||||
Created bond with id: dd88e8d6f9567b32b28e70552aea4419c5dd3307ebae85a284d1fe38904e301a
|
||||
Published demo-record-1.yml with id: bafyreierh3xnfivexlscdwubvczmddsnf46uytyfvrbdhkjzztvsz6ruly
|
||||
...
|
||||
```
|
||||
The published record should be visible in the console.
|
||||
The published records should be visible in the console.
|
||||
|
||||
@@ -30,4 +30,3 @@ config:
|
||||
cli:
|
||||
key: laconicd.mykey
|
||||
address: laconicd.myaddress
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ from stack_orchestrator.opts import opts
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from shutil import copyfile, copytree
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tomli
|
||||
@@ -34,8 +33,9 @@ default_spec_file_content = ""
|
||||
class SetupPhase(Enum):
|
||||
INITIALIZE = 1
|
||||
JOIN = 2
|
||||
CREATE = 3
|
||||
ILLEGAL = 3
|
||||
CONNECT = 3
|
||||
CREATE = 4
|
||||
ILLEGAL = 5
|
||||
|
||||
|
||||
def _client_toml_path(network_dir: Path):
|
||||
@@ -62,29 +62,13 @@ def _get_node_moniker_from_config(network_dir: Path):
|
||||
return moniker
|
||||
|
||||
|
||||
def _get_node_key_from_gentx(gentx_file_name: str):
|
||||
gentx_file_path = Path(gentx_file_name)
|
||||
if gentx_file_path.exists():
|
||||
with open(Path(gentx_file_name), "rb") as f:
|
||||
parsed_json = json.load(f)
|
||||
return parsed_json['body']['messages'][0]['delegator_address']
|
||||
else:
|
||||
print(f"Error: gentx file: {gentx_file_name} does not exist")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _comma_delimited_to_list(list_str: str):
|
||||
return list_str.split(",") if list_str else []
|
||||
|
||||
|
||||
def _get_node_keys_from_gentx_files(gentx_file_list: str):
|
||||
node_keys = []
|
||||
gentx_files = _comma_delimited_to_list(gentx_file_list)
|
||||
for gentx_file in gentx_files:
|
||||
node_key = _get_node_key_from_gentx(gentx_file)
|
||||
if node_key:
|
||||
node_keys.append(node_key)
|
||||
return node_keys
|
||||
def _get_node_keys_from_gentx_files(gentx_address_list: str):
|
||||
gentx_addresses = _comma_delimited_to_list(gentx_address_list)
|
||||
return gentx_addresses
|
||||
|
||||
|
||||
def _copy_gentx_files(network_dir: Path, gentx_file_list: str):
|
||||
@@ -147,6 +131,35 @@ def _enable_cors(config_dir: Path):
|
||||
output_file.write(app_file_content)
|
||||
|
||||
|
||||
def _set_listen_address(config_dir: Path):
|
||||
config_file_path = config_dir.joinpath("config.toml")
|
||||
if not config_file_path.exists():
|
||||
print("Error: config.toml not found")
|
||||
sys.exit(1)
|
||||
with open(config_file_path, "r") as input_file:
|
||||
config_file_content = input_file.read()
|
||||
existing_pattern = r'^laddr = "tcp://127.0.0.1:26657"'
|
||||
replace_with = 'laddr = "tcp://0.0.0.0:26657"'
|
||||
print(f"Replacing in: {config_file_path}")
|
||||
config_file_content = re.sub(existing_pattern, replace_with, config_file_content, flags=re.MULTILINE)
|
||||
with open(config_file_path, "w") as output_file:
|
||||
output_file.write(config_file_content)
|
||||
app_file_path = config_dir.joinpath("app.toml")
|
||||
if not app_file_path.exists():
|
||||
print("Error: app.toml not found")
|
||||
sys.exit(1)
|
||||
with open(app_file_path, "r") as input_file:
|
||||
app_file_content = input_file.read()
|
||||
existing_pattern1 = r'^address = "tcp://localhost:1317"'
|
||||
replace_with1 = 'address = "tcp://0.0.0.0:1317"'
|
||||
app_file_content = re.sub(existing_pattern1, replace_with1, app_file_content, flags=re.MULTILINE)
|
||||
existing_pattern2 = r'^address = "localhost:9090"'
|
||||
replace_with2 = 'address = "0.0.0.0:9090"'
|
||||
app_file_content = re.sub(existing_pattern2, replace_with2, app_file_content, flags=re.MULTILINE)
|
||||
with open(app_file_path, "w") as output_file:
|
||||
output_file.write(app_file_content)
|
||||
|
||||
|
||||
def _phase_from_params(parameters):
|
||||
phase = SetupPhase.ILLEGAL
|
||||
if parameters.initialize_network:
|
||||
@@ -171,6 +184,11 @@ def _phase_from_params(parameters):
|
||||
print("Can't supply --initialize-network or --join-network with --create-network")
|
||||
sys.exit(1)
|
||||
phase = SetupPhase.CREATE
|
||||
elif parameters.connect_network:
|
||||
if parameters.initialize_network or parameters.join_network:
|
||||
print("Can't supply --initialize-network or --join-network with --connect-network")
|
||||
sys.exit(1)
|
||||
phase = SetupPhase.CONNECT
|
||||
return phase
|
||||
|
||||
|
||||
@@ -178,7 +196,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
|
||||
options = opts.o
|
||||
|
||||
currency = "stake" # Does this need to be a parameter?
|
||||
currency = "alnt" # Does this need to be a parameter?
|
||||
|
||||
if options.debug:
|
||||
print(f"parameters: {parameters}")
|
||||
@@ -203,11 +221,12 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
output, status = run_container_command(
|
||||
command_context,
|
||||
"laconicd", f"laconicd init {parameters.node_moniker} --home {laconicd_home_path_in_container}\
|
||||
--chain-id {parameters.chain_id}", mounts)
|
||||
--chain-id {parameters.chain_id} --default-denom {currency}", mounts)
|
||||
if options.debug:
|
||||
print(f"Command output: {output}")
|
||||
|
||||
elif phase == SetupPhase.JOIN:
|
||||
# In the join phase (alternative to connect) we are participating in a genesis ceremony for the chain
|
||||
if not os.path.exists(network_dir):
|
||||
print(f"Error: network directory {network_dir} doesn't exist")
|
||||
sys.exit(1)
|
||||
@@ -222,7 +241,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
output2, status2 = run_container_command(
|
||||
command_context,
|
||||
"laconicd",
|
||||
f"laconicd add-genesis-account {parameters.key_name} 12900000000000000000000{currency}\
|
||||
f"laconicd genesis add-genesis-account {parameters.key_name} 12900000000000000000000{currency}\
|
||||
--home {laconicd_home_path_in_container} --keyring-backend test",
|
||||
mounts)
|
||||
if options.debug:
|
||||
@@ -230,7 +249,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
output3, status3 = run_container_command(
|
||||
command_context,
|
||||
"laconicd",
|
||||
f"laconicd gentx {parameters.key_name} 90000000000{currency} --home {laconicd_home_path_in_container}\
|
||||
f"laconicd genesis gentx {parameters.key_name} 90000000000{currency} --home {laconicd_home_path_in_container}\
|
||||
--chain-id {chain_id} --keyring-backend test",
|
||||
mounts)
|
||||
if options.debug:
|
||||
@@ -240,7 +259,28 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
"laconicd",
|
||||
f"laconicd keys show {parameters.key_name} -a --home {laconicd_home_path_in_container} --keyring-backend test",
|
||||
mounts)
|
||||
print(f"Node validator address: {output4}")
|
||||
print(f"Node account address: {output4}")
|
||||
|
||||
elif phase == SetupPhase.CONNECT:
|
||||
# In the connect phase (named to not conflict with join) we are making a node that syncs a chain with existing genesis.json
|
||||
# but not with validator role. We need this kind of node in order to bootstrap it into a validator after it syncs
|
||||
output1, status1 = run_container_command(
|
||||
command_context, "laconicd", f"laconicd keys add {parameters.key_name} --home {laconicd_home_path_in_container}\
|
||||
--keyring-backend test", mounts)
|
||||
if options.debug:
|
||||
print(f"Command output: {output1}")
|
||||
output2, status2 = run_container_command(
|
||||
command_context,
|
||||
"laconicd",
|
||||
f"laconicd keys show {parameters.key_name} -a --home {laconicd_home_path_in_container} --keyring-backend test",
|
||||
mounts)
|
||||
print(f"Node account address: {output2}")
|
||||
output3, status3 = run_container_command(
|
||||
command_context,
|
||||
"laconicd",
|
||||
f"laconicd cometbft show-validator --home {laconicd_home_path_in_container}",
|
||||
mounts)
|
||||
print(f"Node validator address: {output3}")
|
||||
|
||||
elif phase == SetupPhase.CREATE:
|
||||
if not os.path.exists(network_dir):
|
||||
@@ -259,15 +299,13 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
copyfile(genesis_file_path, os.path.join(network_dir, "config", os.path.basename(genesis_file_path)))
|
||||
else:
|
||||
# We're generating the genesis file
|
||||
if not parameters.gentx_file_list:
|
||||
print("Error: --gentx-files must be supplied")
|
||||
sys.exit(1)
|
||||
# First look in the supplied gentx files for the other nodes' keys
|
||||
other_node_keys = _get_node_keys_from_gentx_files(parameters.gentx_file_list)
|
||||
other_node_keys = _get_node_keys_from_gentx_files(parameters.gentx_address_list)
|
||||
# Add those keys to our genesis, with balances we determine here (why?)
|
||||
for other_node_key in other_node_keys:
|
||||
outputk, statusk = run_container_command(
|
||||
command_context, "laconicd", f"laconicd add-genesis-account {other_node_key} 12900000000000000000000{currency}\
|
||||
command_context, "laconicd", f"laconicd genesis add-genesis-account {other_node_key} \
|
||||
12900000000000000000000{currency}\
|
||||
--home {laconicd_home_path_in_container} --keyring-backend test", mounts)
|
||||
if options.debug:
|
||||
print(f"Command output: {outputk}")
|
||||
@@ -275,7 +313,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
_copy_gentx_files(network_dir, parameters.gentx_file_list)
|
||||
# Now we can run collect-gentxs
|
||||
output1, status1 = run_container_command(
|
||||
command_context, "laconicd", f"laconicd collect-gentxs --home {laconicd_home_path_in_container}", mounts)
|
||||
command_context, "laconicd", f"laconicd genesis collect-gentxs --home {laconicd_home_path_in_container}", mounts)
|
||||
if options.debug:
|
||||
print(f"Command output: {output1}")
|
||||
print(f"Generated genesis file, please copy to other nodes as required: \
|
||||
@@ -284,7 +322,7 @@ def setup(command_context: DeployCommandContext, parameters: LaconicStackSetupCo
|
||||
_remove_persistent_peers(network_dir)
|
||||
# In both cases we validate the genesis file now
|
||||
output2, status1 = run_container_command(
|
||||
command_context, "laconicd", f"laconicd validate-genesis --home {laconicd_home_path_in_container}", mounts)
|
||||
command_context, "laconicd", f"laconicd genesis validate-genesis --home {laconicd_home_path_in_container}", mounts)
|
||||
print(f"validate-genesis result: {output2}")
|
||||
|
||||
else:
|
||||
@@ -319,6 +357,7 @@ def create(deployment_context: DeploymentContext, extra_args):
|
||||
_insert_persistent_peers(deployment_config_dir, initial_persistent_peers)
|
||||
# Enable CORS headers so explorers and so on can talk to the node
|
||||
_enable_cors(deployment_config_dir)
|
||||
_set_listen_address(deployment_config_dir)
|
||||
# Copy the data directory contents into our deployment
|
||||
# TODO: change this to work with non local paths
|
||||
deployment_data_dir = deployment_context.deployment_dir.joinpath("data", "laconicd-data")
|
||||
@@ -331,7 +370,6 @@ def init(command_context: DeployCommandContext):
|
||||
|
||||
|
||||
def get_state(command_context: DeployCommandContext):
|
||||
print("Here we get state")
|
||||
return State.CONFIGURED
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,11 @@ echo "Environment variables:"
|
||||
env
|
||||
# Test laconic stack
|
||||
echo "Running laconic stack test"
|
||||
# Bit of a hack, test the most recent package
|
||||
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||
if [ "$1" == "from-path" ]; then
|
||||
TEST_TARGET_SO="laconic-so"
|
||||
else
|
||||
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||
fi
|
||||
# Set a non-default repo dir
|
||||
export CERC_REPO_BASE_DIR=~/stack-orchestrator-test/repo-base-dir
|
||||
echo "Testing this package: $TEST_TARGET_SO"
|
||||
|
||||
@@ -29,14 +29,14 @@ class DockerDeployer(Deployer):
|
||||
compose_env_file=compose_env_file)
|
||||
self.type = type
|
||||
|
||||
def up(self, detach, services):
|
||||
def up(self, detach, skip_cluster_management, services):
|
||||
if not opts.o.dry_run:
|
||||
try:
|
||||
return self.docker.compose.up(detach=detach, services=services)
|
||||
except DockerException as e:
|
||||
raise DeployerException(e)
|
||||
|
||||
def down(self, timeout, volumes):
|
||||
def down(self, timeout, volumes, skip_cluster_management):
|
||||
if not opts.o.dry_run:
|
||||
try:
|
||||
return self.docker.compose.down(timeout=timeout, volumes=volumes)
|
||||
|
||||
@@ -91,7 +91,7 @@ def create_deploy_context(
|
||||
return DeployCommandContext(stack, cluster_context, deployer)
|
||||
|
||||
|
||||
def up_operation(ctx, services_list, stay_attached=False):
|
||||
def up_operation(ctx, services_list, stay_attached=False, skip_cluster_management=False):
|
||||
global_context = ctx.parent.parent.obj
|
||||
deploy_context = ctx.obj
|
||||
cluster_context = deploy_context.cluster_context
|
||||
@@ -102,18 +102,18 @@ def up_operation(ctx, services_list, stay_attached=False):
|
||||
print(f"Running compose up with container_exec_env: {container_exec_env}, extra_args: {services_list}")
|
||||
for pre_start_command in cluster_context.pre_start_commands:
|
||||
_run_command(global_context, cluster_context.cluster, pre_start_command)
|
||||
deploy_context.deployer.up(detach=not stay_attached, services=services_list)
|
||||
deploy_context.deployer.up(detach=not stay_attached, skip_cluster_management=skip_cluster_management, services=services_list)
|
||||
for post_start_command in cluster_context.post_start_commands:
|
||||
_run_command(global_context, cluster_context.cluster, post_start_command)
|
||||
_orchestrate_cluster_config(global_context, cluster_context.config, deploy_context.deployer, container_exec_env)
|
||||
|
||||
|
||||
def down_operation(ctx, delete_volumes, extra_args_list):
|
||||
def down_operation(ctx, delete_volumes, extra_args_list, skip_cluster_management=False):
|
||||
timeout_arg = None
|
||||
if extra_args_list:
|
||||
timeout_arg = extra_args_list[0]
|
||||
# Specify shutdown timeout (default 10s) to give services enough time to shutdown gracefully
|
||||
ctx.obj.deployer.down(timeout=timeout_arg, volumes=delete_volumes)
|
||||
ctx.obj.deployer.down(timeout=timeout_arg, volumes=delete_volumes, skip_cluster_management=skip_cluster_management)
|
||||
|
||||
|
||||
def status_operation(ctx):
|
||||
|
||||
@@ -50,8 +50,10 @@ class LaconicStackSetupCommand:
|
||||
key_name: str
|
||||
initialize_network: bool
|
||||
join_network: bool
|
||||
connect_network: bool
|
||||
create_network: bool
|
||||
gentx_file_list: str
|
||||
gentx_address_list: str
|
||||
genesis_file: str
|
||||
network_dir: str
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
# 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/>.
|
||||
|
||||
import os
|
||||
from typing import List, Any
|
||||
from stack_orchestrator.deploy.deploy_types import DeployCommandContext, VolumeMapping
|
||||
from stack_orchestrator.util import get_parsed_stack_config, get_yaml, get_pod_list, resolve_compose_file
|
||||
@@ -83,7 +82,9 @@ def run_container_command(ctx: DeployCommandContext, service: str, command: str,
|
||||
docker_output = deployer.run(
|
||||
container_image,
|
||||
["-c", command], entrypoint="sh",
|
||||
user=f"{os.getuid()}:{os.getgid()}",
|
||||
# Current laconicd container has a bug where it crashes when run not as root
|
||||
# Commented out line below is a workaround. Created files end up owned by root on the host
|
||||
# user=f"{os.getuid()}:{os.getgid()}",
|
||||
volumes=docker_volumes
|
||||
)
|
||||
# There doesn't seem to be a way to get an exit code from docker.run()
|
||||
|
||||
@@ -20,11 +20,11 @@ from pathlib import Path
|
||||
class Deployer(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def up(self, detach, services):
|
||||
def up(self, detach, skip_cluster_management, services):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def down(self, timeout, volumes):
|
||||
def down(self, timeout, volumes, skip_cluster_management):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
||||
@@ -61,47 +61,57 @@ def make_deploy_context(ctx) -> DeployCommandContext:
|
||||
cluster_name, env_file, deployment_type)
|
||||
|
||||
|
||||
# TODO: remove legacy up command since it's an alias for start
|
||||
@command.command()
|
||||
@click.option("--stay-attached/--detatch-terminal", default=False, help="detatch or not to see container stdout")
|
||||
@click.option("--skip-cluster-management/--perform-cluster-management",
|
||||
default=False, help="Skip cluster initialization/tear-down (only for kind-k8s deployments)")
|
||||
@click.argument('extra_args', nargs=-1) # help: command: up <service1> <service2>
|
||||
@click.pass_context
|
||||
def up(ctx, stay_attached, extra_args):
|
||||
def up(ctx, stay_attached, skip_cluster_management, extra_args):
|
||||
ctx.obj = make_deploy_context(ctx)
|
||||
services_list = list(extra_args) or None
|
||||
up_operation(ctx, services_list, stay_attached)
|
||||
up_operation(ctx, services_list, stay_attached, skip_cluster_management)
|
||||
|
||||
|
||||
# start is the preferred alias for up
|
||||
@command.command()
|
||||
@click.option("--stay-attached/--detatch-terminal", default=False, help="detatch or not to see container stdout")
|
||||
@click.option("--skip-cluster-management/--perform-cluster-management",
|
||||
default=False, help="Skip cluster initialization/tear-down (only for kind-k8s deployments)")
|
||||
@click.argument('extra_args', nargs=-1) # help: command: up <service1> <service2>
|
||||
@click.pass_context
|
||||
def start(ctx, stay_attached, extra_args):
|
||||
def start(ctx, stay_attached, skip_cluster_management, extra_args):
|
||||
ctx.obj = make_deploy_context(ctx)
|
||||
services_list = list(extra_args) or None
|
||||
up_operation(ctx, services_list, stay_attached)
|
||||
up_operation(ctx, services_list, stay_attached, skip_cluster_management)
|
||||
|
||||
|
||||
# TODO: remove legacy up command since it's an alias for stop
|
||||
@command.command()
|
||||
@click.option("--delete-volumes/--preserve-volumes", default=False, help="delete data volumes")
|
||||
@click.option("--skip-cluster-management/--perform-cluster-management",
|
||||
default=False, help="Skip cluster initialization/tear-down (only for kind-k8s deployments)")
|
||||
@click.argument('extra_args', nargs=-1) # help: command: down <service1> <service2>
|
||||
@click.pass_context
|
||||
def down(ctx, delete_volumes, extra_args):
|
||||
def down(ctx, delete_volumes, skip_cluster_management, extra_args):
|
||||
# Get the stack config file name
|
||||
# TODO: add cluster name and env file here
|
||||
ctx.obj = make_deploy_context(ctx)
|
||||
down_operation(ctx, delete_volumes, extra_args)
|
||||
down_operation(ctx, delete_volumes, extra_args, skip_cluster_management)
|
||||
|
||||
|
||||
# stop is the preferred alias for down
|
||||
@command.command()
|
||||
@click.option("--delete-volumes/--preserve-volumes", default=False, help="delete data volumes")
|
||||
@click.option("--skip-cluster-management/--perform-cluster-management",
|
||||
default=False, help="Skip cluster initialization/tear-down (only for kind-k8s deployments)")
|
||||
@click.argument('extra_args', nargs=-1) # help: command: down <service1> <service2>
|
||||
@click.pass_context
|
||||
def stop(ctx, delete_volumes, extra_args):
|
||||
def stop(ctx, delete_volumes, skip_cluster_management, extra_args):
|
||||
# TODO: add cluster name and env file here
|
||||
ctx.obj = make_deploy_context(ctx)
|
||||
down_operation(ctx, delete_volumes, extra_args)
|
||||
down_operation(ctx, delete_volumes, extra_args, skip_cluster_management)
|
||||
|
||||
|
||||
@command.command()
|
||||
|
||||
@@ -514,6 +514,23 @@ def create_operation(deployment_command_context, spec_file, deployment_dir, netw
|
||||
os.mkdir(destination_script_dir)
|
||||
script_paths = get_pod_script_paths(parsed_stack, pod)
|
||||
_copy_files_to_directory(script_paths, destination_script_dir)
|
||||
if parsed_spec.is_kubernetes_deployment():
|
||||
for configmap in parsed_spec.get_configmaps():
|
||||
source_config_dir = resolve_config_dir(stack_name, configmap)
|
||||
if os.path.exists(source_config_dir):
|
||||
destination_config_dir = deployment_dir_path.joinpath("configmaps", configmap)
|
||||
copytree(source_config_dir, destination_config_dir, dirs_exist_ok=True)
|
||||
else:
|
||||
# TODO: We should probably only do this if the volume is marked :ro.
|
||||
for volume_name, volume_path in parsed_spec.get_volumes().items():
|
||||
source_config_dir = resolve_config_dir(stack_name, volume_name)
|
||||
# Only copy if the source exists and is _not_ empty.
|
||||
if os.path.exists(source_config_dir) and os.listdir(source_config_dir):
|
||||
destination_config_dir = deployment_dir_path.joinpath(volume_path)
|
||||
# Only copy if the destination exists and _is_ empty.
|
||||
if os.path.exists(destination_config_dir) and not os.listdir(destination_config_dir):
|
||||
copytree(source_config_dir, destination_config_dir, dirs_exist_ok=True)
|
||||
|
||||
# Delegate to the stack's Python code
|
||||
# The deploy create command doesn't require a --stack argument so we need to insert the
|
||||
# stack member here.
|
||||
@@ -535,15 +552,17 @@ def create_operation(deployment_command_context, spec_file, deployment_dir, netw
|
||||
@click.option("--chain-id", help="The new chain id")
|
||||
@click.option("--key-name", help="Name for new node key")
|
||||
@click.option("--gentx-files", help="List of comma-delimited gentx filenames from other nodes")
|
||||
@click.option("--gentx-addresses", type=str, help="List of comma-delimited validator addresses for other nodes")
|
||||
@click.option("--genesis-file", help="Genesis file for the network")
|
||||
@click.option("--initialize-network", is_flag=True, default=False, help="Initialize phase")
|
||||
@click.option("--join-network", is_flag=True, default=False, help="Join phase")
|
||||
@click.option("--connect-network", is_flag=True, default=False, help="Connect phase")
|
||||
@click.option("--create-network", is_flag=True, default=False, help="Create phase")
|
||||
@click.option("--network-dir", help="Directory for network files")
|
||||
@click.argument('extra_args', nargs=-1)
|
||||
@click.pass_context
|
||||
def setup(ctx, node_moniker, chain_id, key_name, gentx_files, genesis_file, initialize_network, join_network, create_network,
|
||||
network_dir, extra_args):
|
||||
parmeters = LaconicStackSetupCommand(chain_id, node_moniker, key_name, initialize_network, join_network, create_network,
|
||||
gentx_files, genesis_file, network_dir)
|
||||
def setup(ctx, node_moniker, chain_id, key_name, gentx_files, gentx_addresses, genesis_file, initialize_network, join_network,
|
||||
connect_network, create_network, network_dir, extra_args):
|
||||
parmeters = LaconicStackSetupCommand(chain_id, node_moniker, key_name, initialize_network, join_network, connect_network,
|
||||
create_network, gentx_files, gentx_addresses, genesis_file, network_dir)
|
||||
call_stack_deploy_setup(ctx.obj, parmeters, extra_args)
|
||||
|
||||
@@ -78,28 +78,40 @@ class ClusterInfo:
|
||||
if (opts.o.debug):
|
||||
print(f"Env vars: {self.environment_variables.map}")
|
||||
|
||||
def get_nodeport(self):
|
||||
def get_nodeports(self):
|
||||
nodeports = []
|
||||
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
|
||||
for raw_port in [str(p) for p in service_info["ports"]]:
|
||||
if opts.o.debug:
|
||||
print(f"service port: {raw_port}")
|
||||
if ":" in raw_port:
|
||||
parts = raw_port.split(":")
|
||||
if len(parts) != 2:
|
||||
raise Exception(f"Invalid port definition: {raw_port}")
|
||||
node_port = int(parts[0])
|
||||
pod_port = int(parts[1])
|
||||
else:
|
||||
node_port = None
|
||||
pod_port = int(raw_port)
|
||||
service = client.V1Service(
|
||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-nodeport-{pod_port}"),
|
||||
spec=client.V1ServiceSpec(
|
||||
type="NodePort",
|
||||
ports=[client.V1ServicePort(
|
||||
port=pod_port,
|
||||
target_port=pod_port,
|
||||
node_port=node_port
|
||||
)],
|
||||
selector={"app": self.app_name}
|
||||
)
|
||||
)
|
||||
nodeports.append(service)
|
||||
return nodeports
|
||||
|
||||
def get_ingress(self, use_tls=False, certificate=None, cluster_issuer="letsencrypt-prod"):
|
||||
# No ingress for a deployment that has no http-proxy defined, for now
|
||||
@@ -373,9 +385,12 @@ class ClusterInfo:
|
||||
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
|
||||
)
|
||||
spec = client.V1DeploymentSpec(
|
||||
replicas=1, template=template, selector={
|
||||
replicas=self.spec.get_replicas(),
|
||||
template=template, selector={
|
||||
"matchLabels":
|
||||
{"app": self.app_name}})
|
||||
{"app": self.app_name}
|
||||
}
|
||||
)
|
||||
|
||||
deployment = client.V1Deployment(
|
||||
api_version="apps/v1",
|
||||
|
||||
@@ -16,6 +16,7 @@ from datetime import datetime, timezone
|
||||
|
||||
from pathlib import Path
|
||||
from kubernetes import client, config
|
||||
from typing import List
|
||||
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
|
||||
@@ -51,12 +52,14 @@ class K8sDeployer(Deployer):
|
||||
networking_api: client.NetworkingV1Api
|
||||
k8s_namespace: str = "default"
|
||||
kind_cluster_name: str
|
||||
skip_cluster_management: bool
|
||||
cluster_info: ClusterInfo
|
||||
deployment_dir: Path
|
||||
deployment_context: DeploymentContext
|
||||
|
||||
def __init__(self, type, deployment_context: DeploymentContext, compose_files, compose_project_name, compose_env_file) -> None:
|
||||
self.type = type
|
||||
self.skip_cluster_management = False
|
||||
# TODO: workaround pending refactoring above to cope with being created with a null deployment_context
|
||||
if deployment_context is None:
|
||||
return
|
||||
@@ -182,6 +185,7 @@ class K8sDeployer(Deployer):
|
||||
if len(host_parts) == 2:
|
||||
host_as_wild = f"*.{host_parts[1]}"
|
||||
|
||||
# TODO: resolve method deprecation below
|
||||
now = datetime.utcnow().replace(tzinfo=timezone.utc)
|
||||
fmt = "%Y-%m-%dT%H:%M:%S%z"
|
||||
|
||||
@@ -202,15 +206,16 @@ class K8sDeployer(Deployer):
|
||||
return cert
|
||||
return None
|
||||
|
||||
def up(self, detach, services):
|
||||
def up(self, detach, skip_cluster_management, services):
|
||||
self.skip_cluster_management = skip_cluster_management
|
||||
if not opts.o.dry_run:
|
||||
if self.is_kind():
|
||||
if self.is_kind() and not self.skip_cluster_management:
|
||||
# Create the kind cluster
|
||||
create_cluster(self.kind_cluster_name, self.deployment_dir.joinpath(constants.kind_config_filename))
|
||||
# Ensure the referenced containers are copied into kind
|
||||
load_images_into_kind(self.kind_cluster_name, self.cluster_info.image_set)
|
||||
self.connect_api()
|
||||
if self.is_kind():
|
||||
if self.is_kind() and not self.skip_cluster_management:
|
||||
# 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)
|
||||
@@ -246,8 +251,8 @@ class K8sDeployer(Deployer):
|
||||
if opts.o.debug:
|
||||
print("No ingress configured")
|
||||
|
||||
nodeport: client.V1Service = self.cluster_info.get_nodeport()
|
||||
if nodeport:
|
||||
nodeports: List[client.V1Service] = self.cluster_info.get_nodeports()
|
||||
for nodeport in nodeports:
|
||||
if opts.o.debug:
|
||||
print(f"Sending this nodeport: {nodeport}")
|
||||
if not opts.o.dry_run:
|
||||
@@ -259,7 +264,8 @@ class K8sDeployer(Deployer):
|
||||
print("NodePort created:")
|
||||
print(f"{nodeport_resp}")
|
||||
|
||||
def down(self, timeout, volumes): # noqa: C901
|
||||
def down(self, timeout, volumes, skip_cluster_management): # noqa: C901
|
||||
self.skip_cluster_management = skip_cluster_management
|
||||
self.connect_api()
|
||||
# Delete the k8s objects
|
||||
|
||||
@@ -342,10 +348,10 @@ class K8sDeployer(Deployer):
|
||||
if opts.o.debug:
|
||||
print("No ingress to delete")
|
||||
|
||||
nodeport: client.V1Service = self.cluster_info.get_nodeport()
|
||||
if nodeport:
|
||||
nodeports: List[client.V1Service] = self.cluster_info.get_nodeports()
|
||||
for nodeport in nodeports:
|
||||
if opts.o.debug:
|
||||
print(f"Deleting this nodeport: {ingress}")
|
||||
print(f"Deleting this nodeport: {nodeport}")
|
||||
try:
|
||||
self.core_api.delete_namespaced_service(
|
||||
namespace=self.k8s_namespace,
|
||||
@@ -357,7 +363,7 @@ class K8sDeployer(Deployer):
|
||||
if opts.o.debug:
|
||||
print("No nodeport to delete")
|
||||
|
||||
if self.is_kind():
|
||||
if self.is_kind() and not self.skip_cluster_management:
|
||||
# Destroy the kind cluster
|
||||
destroy_cluster(self.kind_cluster_name)
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@ class Spec:
|
||||
def get_annotations(self):
|
||||
return self.obj.get(constants.annotations_key, {})
|
||||
|
||||
def get_replicas(self):
|
||||
return self.obj.get(constants.replicas_key, 1)
|
||||
|
||||
def get_labels(self):
|
||||
return self.obj.get(constants.labels_key, {})
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ def process_app_deployment_request(
|
||||
image_registry,
|
||||
force_rebuild,
|
||||
fqdn_policy,
|
||||
recreate_on_deploy,
|
||||
logger
|
||||
):
|
||||
logger.log("BEGIN - process_app_deployment_request")
|
||||
@@ -165,6 +166,7 @@ def process_app_deployment_request(
|
||||
deploy_to_k8s(
|
||||
deployment_record,
|
||||
deployment_dir,
|
||||
recreate_on_deploy,
|
||||
logger
|
||||
)
|
||||
|
||||
@@ -220,12 +222,13 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
@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("--force-rebuild", help="Rebuild even if the image already exists.", is_flag=True)
|
||||
@click.option("--recreate-on-deploy", help="Remove and recreate deployments instead of updating them.", is_flag=True)
|
||||
@click.option("--log-dir", help="Output build/deployment logs to directory.", default=None)
|
||||
@click.pass_context
|
||||
def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir, # noqa: C901
|
||||
request_id, discover, state_file, only_update_state,
|
||||
dns_suffix, fqdn_policy, record_namespace_dns, record_namespace_deployments, dry_run,
|
||||
include_tags, exclude_tags, force_rebuild, log_dir):
|
||||
include_tags, exclude_tags, force_rebuild, recreate_on_deploy, log_dir):
|
||||
if request_id and discover:
|
||||
print("Cannot specify both --request-id and --discover", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
@@ -274,34 +277,43 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
||||
requests_by_name = {}
|
||||
skipped_by_name = {}
|
||||
for r in requests:
|
||||
if r.id in previous_requests and previous_requests[r.id].get("status", "") != "RETRY":
|
||||
print(f"Skipping request {r.id}, we've already seen it.")
|
||||
continue
|
||||
status = None
|
||||
try:
|
||||
if r.id in previous_requests and previous_requests[r.id].get("status", "") != "RETRY":
|
||||
print(f"Skipping request {r.id}, we've already seen it.")
|
||||
continue
|
||||
|
||||
app = laconic.get_record(r.attributes.application)
|
||||
if not app:
|
||||
print("Skipping request %s, cannot locate app." % r.id)
|
||||
continue
|
||||
app = laconic.get_record(r.attributes.application)
|
||||
if not app:
|
||||
print(f"Skipping request {r.id}, cannot locate app.")
|
||||
status = "SEEN"
|
||||
continue
|
||||
|
||||
requested_name = r.attributes.dns
|
||||
if not requested_name:
|
||||
requested_name = generate_hostname_for_app(app)
|
||||
print("Generating name %s for request %s." % (requested_name, r.id))
|
||||
requested_name = r.attributes.dns
|
||||
if not requested_name:
|
||||
requested_name = generate_hostname_for_app(app)
|
||||
print("Generating name %s for request %s." % (requested_name, r.id))
|
||||
|
||||
if requested_name in skipped_by_name or requested_name in requests_by_name:
|
||||
print("Ignoring request %s, it has been superseded." % r.id)
|
||||
continue
|
||||
if requested_name in skipped_by_name or requested_name in requests_by_name:
|
||||
print("Ignoring request %s, it has been superseded." % r.id)
|
||||
continue
|
||||
|
||||
if skip_by_tag(r, include_tags, exclude_tags):
|
||||
print("Skipping request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id,
|
||||
include_tags,
|
||||
exclude_tags,
|
||||
r.attributes.tags))
|
||||
skipped_by_name[requested_name] = r
|
||||
continue
|
||||
if skip_by_tag(r, include_tags, exclude_tags):
|
||||
print("Skipping request %s, filtered by tag (include %s, exclude %s, present %s)" % (r.id,
|
||||
include_tags,
|
||||
exclude_tags,
|
||||
r.attributes.tags))
|
||||
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
|
||||
print("Found request %s to run application %s on %s." % (r.id, r.attributes.application, requested_name))
|
||||
requests_by_name[requested_name] = r
|
||||
except Exception as e:
|
||||
print(f"ERROR examining request {r.id}: " + str(e))
|
||||
status = "ERROR"
|
||||
finally:
|
||||
if status:
|
||||
dump_known_requests(state_file, [r], status)
|
||||
|
||||
# Find deployments.
|
||||
deployments = laconic.app_deployments()
|
||||
@@ -365,6 +377,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
|
||||
image_registry,
|
||||
force_rebuild,
|
||||
fqdn_policy,
|
||||
recreate_on_deploy,
|
||||
logger
|
||||
)
|
||||
status = "DEPLOYED"
|
||||
|
||||
@@ -172,7 +172,7 @@ class LaconicRegistryClient:
|
||||
name_or_id,
|
||||
]
|
||||
|
||||
parsed = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args))]
|
||||
parsed = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
if len(parsed):
|
||||
self._add_to_cache(parsed)
|
||||
return parsed[0]
|
||||
@@ -318,17 +318,24 @@ def push_container_image(deployment_dir, logger):
|
||||
logger.log("Finished pushing images.")
|
||||
|
||||
|
||||
def deploy_to_k8s(deploy_record, deployment_dir, logger):
|
||||
if not deploy_record:
|
||||
command = "start"
|
||||
else:
|
||||
command = "update"
|
||||
|
||||
def deploy_to_k8s(deploy_record, deployment_dir, recreate, logger):
|
||||
logger.log("Deploying to k8s ...")
|
||||
logger.log(f"Running {command} command on deployment dir: {deployment_dir}")
|
||||
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command],
|
||||
stdout=logger.file, stderr=logger.file)
|
||||
result.check_returncode()
|
||||
|
||||
if recreate:
|
||||
commands_to_run = ["stop", "start"]
|
||||
else:
|
||||
if not deploy_record:
|
||||
commands_to_run = ["start"]
|
||||
else:
|
||||
commands_to_run = ["update"]
|
||||
|
||||
for command in commands_to_run:
|
||||
logger.log(f"Running {command} command on deployment dir: {deployment_dir}")
|
||||
result = subprocess.run([sys.argv[0], "deployment", "--dir", deployment_dir, command],
|
||||
stdout=logger.file, stderr=logger.file)
|
||||
result.check_returncode()
|
||||
logger.log(f"Finished {command} command on deployment dir: {deployment_dir}")
|
||||
|
||||
logger.log("Finished deploying to k8s.")
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
set -e
|
||||
echo "Running stack-orchestrator Ethereum plugeth fixturenet test"
|
||||
# Bit of a hack, test the most recent package
|
||||
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||
CERC_STACK_NAME=fixturenet-plugeth-tx
|
||||
# Set a new unique repo dir
|
||||
export CERC_REPO_BASE_DIR=$(mktemp -d stack-orchestrator-fixturenet-eth-test.XXXXXXXXXX)
|
||||
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"
|
||||
$TEST_TARGET_SO --stack $CERC_STACK_NAME setup-repositories
|
||||
echo "Building containers"
|
||||
$TEST_TARGET_SO --stack $CERC_STACK_NAME build-containers
|
||||
echo "Images in registry:"
|
||||
docker image ls
|
||||
echo "Deploying the cluster"
|
||||
$TEST_TARGET_SO --stack $CERC_STACK_NAME deploy up
|
||||
# Verify that the fixturenet is up and running
|
||||
$TEST_TARGET_SO --stack $CERC_STACK_NAME deploy ps
|
||||
$TEST_TARGET_SO --stack $CERC_STACK_NAME deploy exec fixturenet-eth-bootnode-lighthouse /scripts/status-internal.sh
|
||||
initial_block_number=$($TEST_TARGET_SO --stack fixturenet-plugeth-tx deploy exec foundry "cast block-number")
|
||||
# Check that the block number increases some time later
|
||||
sleep 12
|
||||
subsequent_block_number=$($TEST_TARGET_SO --stack $CERC_STACK_NAME deploy exec foundry "cast block-number")
|
||||
block_number_difference=$((subsequent_block_number - initial_block_number))
|
||||
# Block height difference should be between 1 and some small number
|
||||
if [[ $block_number_difference -gt 1 && $block_number_difference -lt 10 ]]; then
|
||||
echo "Test passed"
|
||||
test_result=0
|
||||
else
|
||||
echo "Test failed: block numbers were ${initial_block_number} and ${subsequent_block_number}"
|
||||
test_result=1
|
||||
fi
|
||||
$TEST_TARGET_SO --stack $CERC_STACK_NAME deploy down
|
||||
echo "Removing cloned repositories"
|
||||
rm -rf $CERC_REPO_BASE_DIR
|
||||
exit $test_result
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
echo "$(date +"%Y-%m-%d %T"): Running stack-orchestrator Ethereum fixturenet test"
|
||||
# Bit of a hack, test the most recent package
|
||||
TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 )
|
||||
# Set a new unique repo dir
|
||||
export CERC_REPO_BASE_DIR=$(mktemp -d stack-orchestrator-fixturenet-eth-test.XXXXXXXXXX)
|
||||
echo "$(date +"%Y-%m-%d %T"): Testing this package: $TEST_TARGET_SO"
|
||||
echo "$(date +"%Y-%m-%d %T"): Test version command"
|
||||
reported_version_string=$( $TEST_TARGET_SO version )
|
||||
echo "$(date +"%Y-%m-%d %T"): Version reported is: ${reported_version_string}"
|
||||
echo "$(date +"%Y-%m-%d %T"): Cloning repositories into: $CERC_REPO_BASE_DIR"
|
||||
$TEST_TARGET_SO --stack fixturenet-eth setup-repositories
|
||||
echo "$(date +"%Y-%m-%d %T"): Building containers"
|
||||
$TEST_TARGET_SO --stack fixturenet-eth build-containers
|
||||
echo "$(date +"%Y-%m-%d %T"): Starting stack"
|
||||
$TEST_TARGET_SO --stack fixturenet-eth deploy up
|
||||
echo "$(date +"%Y-%m-%d %T"): Stack started"
|
||||
# Verify that the fixturenet is up and running
|
||||
$TEST_TARGET_SO --stack fixturenet-eth deploy ps
|
||||
# echo "$(date +"%Y-%m-%d %T"): Getting stack status"
|
||||
# $TEST_TARGET_SO --stack fixturenet-eth deploy exec fixturenet-eth-bootnode-lighthouse /scripts/status-internal.sh
|
||||
|
||||
timeout=900 # 15 minutes
|
||||
echo "$(date +"%Y-%m-%d %T"): Getting initial block number. Timeout set to $timeout seconds"
|
||||
start_time=$(date +%s)
|
||||
elapsed_time=0
|
||||
initial_block_number=0
|
||||
while [ "$initial_block_number" -eq 0 ] && [ $elapsed_time -lt $timeout ]; do
|
||||
sleep 10
|
||||
echo "$(date +"%Y-%m-%d %T"): Waiting for initial block..."
|
||||
initial_block_number=$($TEST_TARGET_SO --stack fixturenet-eth deploy exec foundry "cast block-number")
|
||||
current_time=$(date +%s)
|
||||
elapsed_time=$((current_time - start_time))
|
||||
done
|
||||
|
||||
subsequent_block_number=$initial_block_number
|
||||
|
||||
# if initial block was 0 after timeout, assume chain did not start successfully and skip finding subsequent block
|
||||
if [[ $initial_block_number -gt 0 ]]; then
|
||||
timeout=300
|
||||
echo "$(date +"%Y-%m-%d %T"): Getting subsequent block number. Timeout set to $timeout seconds"
|
||||
start_time=$(date +%s)
|
||||
elapsed_time=0
|
||||
# wait for 5 blocks or timeout
|
||||
while [ "$subsequent_block_number" -le $((initial_block_number + 5)) ] && [ $elapsed_time -lt $timeout ]; do
|
||||
sleep 10
|
||||
echo "$(date +"%Y-%m-%d %T"): Waiting for five blocks or $timeout seconds..."
|
||||
subsequent_block_number=$($TEST_TARGET_SO --stack fixturenet-eth deploy exec foundry "cast block-number")
|
||||
current_time=$(date +%s)
|
||||
elapsed_time=$((current_time - start_time))
|
||||
done
|
||||
fi
|
||||
|
||||
# will return 0 if either of the above loops timed out
|
||||
block_number_difference=$((subsequent_block_number - initial_block_number))
|
||||
|
||||
echo "$(date +"%Y-%m-%d %T"): Results of block height queries:"
|
||||
echo "Initial block height: $initial_block_number"
|
||||
echo "Subsequent block height: $subsequent_block_number"
|
||||
|
||||
# Block height difference should be between 1 and some small number
|
||||
if [[ $block_number_difference -gt 1 && $block_number_difference -lt 100 ]]; then
|
||||
echo "Test passed"
|
||||
test_result=0
|
||||
else
|
||||
echo "Test failed: block numbers were ${initial_block_number} and ${subsequent_block_number}"
|
||||
echo "Logs from stack:"
|
||||
$TEST_TARGET_SO --stack fixturenet-eth deploy logs
|
||||
test_result=1
|
||||
fi
|
||||
$TEST_TARGET_SO --stack fixturenet-eth deploy down --delete-volumes
|
||||
echo "$(date +"%Y-%m-%d %T"): Removing cloned repositories"
|
||||
rm -rf $CERC_REPO_BASE_DIR
|
||||
echo "$(date +"%Y-%m-%d %T"): Test finished"
|
||||
exit $test_result
|
||||
@@ -4,6 +4,10 @@ if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
set_ownership () {
|
||||
sudo chown $USER: -R $1
|
||||
}
|
||||
|
||||
node_count=4
|
||||
node_dir_prefix="laconic-network-dir"
|
||||
chain_id="laconic_81337-6"
|
||||
@@ -15,7 +19,7 @@ do
|
||||
node_network_dir=${node_dir_prefix}${i}
|
||||
if [[ -d $node_network_dir ]]; then
|
||||
echo "Deleting ${node_network_dir}"
|
||||
rm -rf ${node_network_dir}
|
||||
sudo rm -rf ${node_network_dir}
|
||||
fi
|
||||
done
|
||||
echo "Deleting any existing deployments..."
|
||||
@@ -39,6 +43,7 @@ do
|
||||
node_network_dir=${node_dir_prefix}${i}
|
||||
node_moniker=${node_moniker_prefix}${i}
|
||||
laconic-so --stack mainnet-laconic deploy setup --network-dir ${node_network_dir} --initialize-network --chain-id ${chain_id} --node-moniker ${node_moniker}
|
||||
set_ownership ${node_network_dir}
|
||||
done
|
||||
|
||||
echo "Joining ${node_count} nodes to the network..."
|
||||
@@ -47,6 +52,7 @@ do
|
||||
node_network_dir=${node_dir_prefix}${i}
|
||||
node_moniker=${node_moniker_prefix}${i}
|
||||
laconic-so --stack mainnet-laconic deploy setup --network-dir ${node_network_dir} --join-network --key-name ${node_moniker}
|
||||
set_ownership ${node_network_dir}
|
||||
done
|
||||
|
||||
echo "Merging ${node_count} nodes genesis txns..."
|
||||
@@ -57,11 +63,15 @@ for (( i=2 ; i<=$node_count ; i++ ));
|
||||
do
|
||||
node_network_dir=${node_dir_prefix}${i}
|
||||
node_gentx_file=$(ls ${node_network_dir}/config/gentx/*.json)
|
||||
node_gentx_address=$(grep address ${node_network_dir}/config/genesis.json | head -1 | cut -d '"' -f 4)
|
||||
gentx_files+=${delimeter}${node_gentx_file}
|
||||
gentx_addresses+=${delimeter}${node_gentx_address}
|
||||
delimeter=","
|
||||
done
|
||||
echo "gentx files:"
|
||||
echo ${gentx_files}
|
||||
# Generate the genesis file on node 1
|
||||
laconic-so --stack mainnet-laconic deploy setup --network-dir ${node_dir_prefix}1 --create-network --gentx-files ${gentx_files}
|
||||
laconic-so --stack mainnet-laconic deploy setup --network-dir ${node_dir_prefix}1 --create-network --gentx-files ${gentx_files} --gentx-addresses ${gentx_addresses}
|
||||
genesis_file=${node_dir_prefix}1/config/genesis.json
|
||||
# Now import the genesis file to the other nodes
|
||||
for (( i=2 ; i<=$node_count ; i++ ));
|
||||
@@ -69,6 +79,7 @@ do
|
||||
echo "Importing genesis.json into node ${i}"
|
||||
node_network_dir=${node_dir_prefix}${i}
|
||||
laconic-so --stack mainnet-laconic deploy setup --network-dir ${node_network_dir} --create-network --genesis-file ${genesis_file}
|
||||
set_ownership ${node_network_dir}
|
||||
done
|
||||
|
||||
# Create deployments
|
||||
|
||||
Reference in New Issue
Block a user