Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add distinct sudo/non-sudo and seed_derivation modes #122

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/config/network_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ def network_sudo_seed():
return get_var_from_env('SUDO_SEED')


def sudo_mode():
return network_sudo_seed() is not None


def derivation_seed_mode():
return derivation_root_seed() is not None


def get_relay_chain_ss58_format():
relay_chain_ss58_format = get_var_from_env('RELAY_CHAIN_SS58_FORMAT')
if relay_chain_ss58_format is None:
Expand Down
1 change: 1 addition & 0 deletions app/lib/collator_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

log = logging.getLogger(__name__)


def get_derived_collator_seed(node_name):
root_seed = derivation_root_seed()
return root_seed + "//collator//" + node_name
Expand Down
22 changes: 14 additions & 8 deletions app/lib/network_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import datetime, timedelta

from app.config.network_configuration import get_relay_chain_rpc_url, network_sudo_seed, derivation_root_seed, \
node_http_endpoint, get_network, relay_chain_consensus, node_ws_endpoint
node_http_endpoint, get_network, relay_chain_consensus, node_ws_endpoint, derivation_seed_mode
from app.lib.balance_utils import fund_accounts
from app.lib.collator_account import get_derived_moon_collator_account, get_derived_collator_account, \
get_derived_collator_session_keys
Expand Down Expand Up @@ -33,15 +33,18 @@
log = logging.getLogger(__name__)

relay_chain_network_name = get_network()

is_derivation_seed_mode = derivation_seed_mode()

def get_validator_account_from_pod(pod):
node_name = pod.metadata.name
validator_account = pod.metadata.labels.get('validatorAccount', None)
if validator_account:
return validator_account
else:
return get_derived_node_stash_account_address(derivation_root_seed(), node_name)
if is_derivation_seed_mode:
return get_derived_node_stash_account_address(derivation_root_seed(), node_name)
else:
return None


def get_collator_account_from_pod(pod):
Expand All @@ -52,12 +55,15 @@ def get_collator_account_from_pod(pod):
return collator_account
else:
chain = pod.metadata.labels['chain']
if chain.startswith("moon"):
return get_derived_moon_collator_account(node_name)
if is_derivation_seed_mode:
if chain.startswith("moon"):
return get_derived_moon_collator_account(node_name)
else:
# 42 is the ss58 format default value, see https://polkadot.js.org/docs/keyring/start/ss58/
ss58_format = pod.metadata.labels.get('ss58Format','42')
return get_derived_collator_account(node_name, ss58_format)
else:
# 42 is the ss58 format default value, see https://polkadot.js.org/docs/keyring/start/ss58/
ss58_format = pod.metadata.labels.get('ss58Format','42')
return get_derived_collator_account(node_name, ss58_format)
return None


def get_validator_status(node_stash_account_address, validator_set, validators_to_add, validators_to_retire):
Expand Down
85 changes: 85 additions & 0 deletions app/routers/read_apis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import logging

from fastapi import APIRouter, Path, Query
from fastapi.responses import JSONResponse, PlainTextResponse, Response

from app.lib.log_utils import get_node_pod_logs
from app.lib.network_utils import list_substrate_nodes, list_validators, list_parachains, list_parachain_collators, \
get_substrate_node
from app.lib.runtime_utils import get_relay_runtime, get_relay_active_configuration, get_parachain_runtime, \
get_relaychain_metadata, get_parachain_metadata

log = logging.getLogger('router_read_apis')

router = APIRouter(prefix="/api")


@router.get("/nodes")
async def get_nodes(
statefulset: str = Query(default="", description="To restrict the displayed nodes to a single StatefulSet")
):
return JSONResponse(list_substrate_nodes(statefulset))


@router.get("/nodes/{node_name}")
async def get_nodes(
node_name: str = Path(description="Name of the node"),
):
return JSONResponse(get_substrate_node(node_name))


@router.get("/nodes/{node_name}/logs", response_class=PlainTextResponse, )
async def get_node_logs(
node_name: str = Path(description="Name of the node"),
):
node_logs = await get_node_pod_logs(node_name)
return PlainTextResponse(node_logs)


@router.get("/validators")
async def get_validators(
statefulset: str = Query(default="", description="To restrict the displayed nodes to a single StatefulSet")
):
return JSONResponse(list_validators(statefulset))


@router.get("/parachains")
async def get_parachains():
return JSONResponse(list_parachains())


@router.get("/collators/{para_id}")
async def get_collators(
para_id: str = Path(description="ID of the parachain for which to get collators"),
statefulset: str = Query(default="", description="To restrict the displayed nodes to a single StatefulSet")
):
return JSONResponse(list_parachain_collators(para_id, statefulset))


@router.get("/runtime")
async def get_runtime():
return JSONResponse(get_relay_runtime())


@router.get("/runtime/configuration")
async def get_runtime_configuration():
return JSONResponse(get_relay_active_configuration())


@router.get("/runtime/metadata")
async def get_relaychain_runtime_metadata():
return Response(content=get_relaychain_metadata(), media_type="application/octet-stream")


@router.get("/parachains/{para_id}/runtime")
async def get_runtime_parachain(
para_id: str = Path(description="ID of the parachain for which to get runtime info")
):
return JSONResponse(get_parachain_runtime(para_id))


@router.get("/parachains/{para_id}/runtime/metadata")
async def get_parachain_runtime_metadata(
para_id: str = Path(description="ID of the parachain for which to get runtime metadata")
):
return Response(content=get_parachain_metadata(para_id), media_type="application/octet-stream")
91 changes: 9 additions & 82 deletions app/routers/apis.py → app/routers/sudo_apis.py
Original file line number Diff line number Diff line change
@@ -1,87 +1,33 @@
import asyncio
import logging
from typing import Any, Dict
from typing import Dict, Any

from fastapi import APIRouter, Path, Query, HTTPException, File, UploadFile
from fastapi.responses import JSONResponse, PlainTextResponse, Response
from fastapi.responses import PlainTextResponse
from substrateinterface import Keypair

from app.config.network_configuration import network_sudo_seed
from app.lib.balance_utils import teleport_funds, transfer_funds
from app.lib.cron_tasks import list_cron_tasks, exec_cron_task
from app.lib.kubernetes_client import list_validator_stateful_sets
from app.lib.log_utils import get_node_pod_logs
from app.lib.network_utils import list_substrate_nodes, list_validators, list_parachains, list_parachain_collators, \
register_statefulset_validators, deregister_statefulset_validators, deregister_validator_addresses, \
from app.lib.network_utils import list_parachains, register_statefulset_validators, deregister_statefulset_validators, \
deregister_validator_addresses, \
rotate_nodes_session_keys, register_statefulset_collators, onboard_parachain_by_id, \
offboard_parachain_by_id, deregister_statefulset_collators, get_substrate_node, \
register_validator_nodes, register_validator_addresses, deregister_validator_nodes, register_collator_nodes, \
deregister_collator_nodes, add_invulnerable_collator, remove_invulnerable_collator, \
set_collator_nodes_keys_on_chain, add_invulnerable_collators, remove_invulnerable_collators
from app.lib.parachain_manager import parachain_runtime_upgrade
from app.lib.runtime_utils import get_relay_runtime, get_relay_active_configuration, update_relay_configuration, \
get_parachain_runtime, runtime_upgrade, get_relaychain_metadata, get_parachain_metadata
from app.lib.staking_utils import get_validator_nominator_mnemonic, staking_create_nominator, \
create_nominators_for_validator_node
from app.lib.runtime_utils import update_relay_configuration, \
runtime_upgrade
from app.lib.staking_utils import create_nominators_for_validator_node
from app.lib.substrate import get_relay_chain_client

log = logging.getLogger('router_apis')
log = logging.getLogger('router_read_apis')

log = logging.getLogger('router_update_apis')
router = APIRouter(prefix="/api")


@router.get("/nodes")
async def get_nodes(
statefulset: str = Query(default="", description="To restrict the displayed nodes to a single StatefulSet")
):
return JSONResponse(list_substrate_nodes(statefulset))


@router.get("/nodes/{node_name}")
async def get_nodes(
node_name: str = Path(description="Name of the node"),
):
return JSONResponse(get_substrate_node(node_name))


@router.get("/nodes/{node_name}/logs", response_class=PlainTextResponse, )
async def get_node_logs(
node_name: str = Path(description="Name of the node"),
):
node_logs = await get_node_pod_logs(node_name)
return PlainTextResponse(node_logs)


@router.get("/validators")
async def get_validators(
statefulset: str = Query(default="", description="To restrict the displayed nodes to a single StatefulSet")
):
return JSONResponse(list_validators(statefulset))


@router.get("/parachains")
async def get_parachains():
return JSONResponse(list_parachains())


@router.get("/collators/{para_id}")
async def get_collators(
para_id: str = Path(description="ID of the parachain for which to get collators"),
statefulset: str = Query(default="", description="To restrict the displayed nodes to a single StatefulSet")
):
return JSONResponse(list_parachain_collators(para_id, statefulset))


@router.get("/runtime")
async def get_runtime():
return JSONResponse(get_relay_runtime())


@router.get("/runtime/configuration")
async def get_runtime_configuration():
return JSONResponse(get_relay_active_configuration())


@router.post("/runtime/configuration")
async def update_runtime_configuration(new_configuration_keys: Dict[str, Any]):
for key, value in new_configuration_keys.items():
Expand All @@ -91,25 +37,6 @@ async def update_runtime_configuration(new_configuration_keys: Dict[str, Any]):
return PlainTextResponse("OK")


@router.get("/runtime/metadata")
async def get_relaychain_runtime_metadata():
return Response(content=get_relaychain_metadata(), media_type="application/octet-stream")


@router.get("/parachains/{para_id}/runtime")
async def get_runtime_parachain(
para_id: str = Path(description="ID of the parachain for which to get runtime info")
):
return JSONResponse(get_parachain_runtime(para_id))


@router.get("/parachains/{para_id}/runtime/metadata")
async def get_parachain_runtime_metadata(
para_id: str = Path(description="ID of the parachain for which to get runtime metadata")
):
return Response(content=get_parachain_metadata(para_id), media_type="application/octet-stream")


@router.post("/validators/register")
async def register_validators(
statefulset: str = Query(default=None, description="Name of the StatefulSet containing the nodes to be registered"),
Expand Down
4 changes: 2 additions & 2 deletions app/routers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from starlette.templating import Jinja2Templates

from app import __version__
from app.config.network_configuration import get_node_logs_link, get_network
from app.config.network_configuration import get_node_logs_link, get_network, sudo_mode
from app.lib.kubernetes_client import get_pod_details, list_validator_stateful_sets, list_parachain_collator_stateful_sets
from app.lib.network_utils import list_substrate_nodes, list_validators, get_session_queued_keys, list_parachains, \
list_parachain_collators, get_substrate_node
Expand All @@ -19,7 +19,7 @@
@router.get("/", response_class=HTMLResponse, include_in_schema=False)
@router.get("/index.html", response_class=HTMLResponse, include_in_schema=False)
async def homepage(request: Request):
return templates.TemplateResponse('index.html', {'request': request, 'network': network, 'version': __version__})
return templates.TemplateResponse('index.html', {'request': request, 'network': network, 'version': __version__, 'sudo_mode': sudo_mode()})


@router.get("/nodes", response_class=HTMLResponse, include_in_schema=False)
Expand Down
3 changes: 2 additions & 1 deletion app/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</p>
</body>
<footer>
Version: {{ version }}
<p>Version: {{ version }}</p>
<p>Sudo Mode: {{ sudo_mode }}</p>
</footer>
</html>
25 changes: 16 additions & 9 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,37 @@

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from starlette.responses import PlainTextResponse
from kubernetes import config as kubernetes_config
from starlette.responses import JSONResponse

from app import __version__
from app.routers import views, apis
from app.config import log_config
from app.config.network_configuration import sudo_mode
from app.config.settings import settings
from kubernetes import config as kubernetes_config
from app.routers import views, read_apis, sudo_apis


# Disable health check logs (https://stackoverflow.com/a/70810102)
class EndpointFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
return record.args and len(record.args) >= 3 and record.args[2] != "/health"

app = FastAPI(title=settings.APP_NAME, version=__version__)
app.include_router(apis.router)
app.include_router(views.router)
app.mount("/static", StaticFiles(directory="app/static"), name="static")

# Setup Logging
dictConfig(log_config.logger_config)
log = logging.getLogger(__name__)
logging.getLogger("uvicorn.access").addFilter(EndpointFilter())

app = FastAPI(title=settings.APP_NAME, version=__version__)

app.include_router(read_apis.router)
if sudo_mode():
log.info('sudo mode enabled')
app.include_router(sudo_apis.router)

app.include_router(views.router)
app.mount("/static", StaticFiles(directory="app/static"), name="static")


# Connect to Kubernetes
try:
kubernetes_config.load_incluster_config()
Expand All @@ -35,4 +42,4 @@ def filter(self, record: logging.LogRecord) -> bool:

@app.get("/health")
async def health():
return PlainTextResponse('UP')
return JSONResponse({'status': 'UP', 'sudoMode': sudo_mode})
Loading
Loading