Skip to content

Commit f08a6c0

Browse files
committed
[CLI] New CLI tool for node operators
We now provide a CLI tool that integrates all the operations commonly performed by node operators. Currently, this CLI allows to: * generate private keys for the node, replacing a functionality that was implemented in the CCN main app directly. * run migrations, replacing the config updater script.
1 parent 1c379e3 commit f08a6c0

File tree

15 files changed

+371
-214
lines changed

15 files changed

+371
-214
lines changed

deployment/migrations/config_updater.py

-164
This file was deleted.

setup.cfg

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ install_requires =
6262
sentry-sdk==1.5.11
6363
setproctitle==1.2.2
6464
substrate-interface==1.1.7
65+
typer==0.4.1
6566
ujson==5.1.0 # required by aiocache
6667
urllib3==1.26.8
6768
uvloop==0.16.0
@@ -94,6 +95,7 @@ testing =
9495
pytest-aiohttp
9596
pytest-asyncio
9697
pytest-mock
98+
types-pyyaml
9799
types-requests
98100
types-setuptools
99101
nuls2 =
@@ -109,6 +111,7 @@ docs =
109111
# Add here console scripts like:
110112
console_scripts =
111113
pyaleph = aleph.commands:run
114+
ccn_cli = aleph.ccn_cli.main:app
112115
# For example:
113116
# console_scripts =
114117
# fibonacci = pyaleph.skeleton:run
File renamed without changes.

src/aleph/ccn_cli/cli_config.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Global configuration object for the CLI. Use the `get_cli_config()` method
3+
to access and modify the configuration.
4+
"""
5+
6+
from dataclasses import dataclass
7+
from pathlib import Path
8+
9+
10+
@dataclass
11+
class CliConfig:
12+
config_file_path: Path
13+
key_dir: Path
14+
verbose: bool

src/aleph/ccn_cli/commands/__init__.py

Whitespace-only changes.

src/aleph/ccn_cli/commands/keys.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import typer
2+
3+
from aleph.ccn_cli.cli_config import CliConfig
4+
from typing import cast
5+
from aleph.ccn_cli.services.keys import generate_keypair, save_keys
6+
7+
keys_ns = typer.Typer()
8+
9+
10+
@keys_ns.command()
11+
def generate(ctx: typer.Context):
12+
"""
13+
Generates a new set of private/public keys for the Core Channel Node.
14+
The keys will be created in the key directory. You can modify the destination
15+
by using the --key-dir option.
16+
"""
17+
cli_config = cast(CliConfig, ctx.obj)
18+
print(cli_config)
19+
20+
typer.echo(f"Generating a key pair in {cli_config.key_dir.absolute()}...")
21+
key_pair = generate_keypair()
22+
save_keys(key_pair, str(cli_config.key_dir))
23+
typer.echo("Done.")
24+
25+
26+
@keys_ns.command()
27+
def show(ctx: typer.Context):
28+
"""
29+
Prints the private key of the node.
30+
"""
31+
cli_config = cast(CliConfig, ctx.obj)
32+
33+
key_path = cli_config.key_dir / "node-secret.key"
34+
if not key_path.exists():
35+
typer.echo(
36+
f"'{key_path.absolute()}' does not exist. Did you run 'keys generate'?",
37+
err=True,
38+
)
39+
raise typer.Exit(code=1)
40+
41+
if not key_path.is_file():
42+
typer.echo(f"'{key_path}' is not a file.", err=True)
43+
raise typer.Exit(code=1)
44+
45+
with key_path.open() as f:
46+
typer.echo(f.read())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import asyncio
2+
from pathlib import Path
3+
from traceback import format_exc
4+
from typing import Optional
5+
from typing import cast
6+
7+
import typer
8+
9+
from aleph.ccn_cli.cli_config import CliConfig
10+
from .migration_runner import run_migrations
11+
12+
migrations_ns = typer.Typer()
13+
14+
15+
FILTER_SCRIPTS_HELP = (
16+
"A filter for migration scripts. If specified, only the files "
17+
"matching the provided glob expression will be run."
18+
)
19+
20+
KEY_FILE_HELP = (
21+
"Path to the private key file, if any. "
22+
"Only used to upgrade the key to the latest format."
23+
)
24+
25+
26+
def run_migration_command(
27+
cli_config: CliConfig,
28+
command: str,
29+
filter_scripts: Optional[str],
30+
key_file: Optional[Path],
31+
):
32+
try:
33+
asyncio.run(
34+
run_migrations(
35+
cli_config=cli_config,
36+
command=command,
37+
filter_scripts=filter_scripts,
38+
key_file=key_file,
39+
)
40+
)
41+
except Exception as e:
42+
typer.echo(f"{command} failed: {e}.", err=True)
43+
if cli_config.verbose:
44+
typer.echo(format_exc())
45+
raise typer.Exit(code=1)
46+
47+
48+
@migrations_ns.command()
49+
def upgrade(
50+
ctx: typer.Context,
51+
filter_scripts: Optional[str] = typer.Option(
52+
None,
53+
help=FILTER_SCRIPTS_HELP,
54+
),
55+
key_file: Optional[Path] = typer.Option(
56+
None,
57+
help=KEY_FILE_HELP,
58+
),
59+
):
60+
cli_config = cast(CliConfig, ctx.obj)
61+
run_migration_command(
62+
cli_config=cli_config,
63+
command="upgrade",
64+
filter_scripts=filter_scripts,
65+
key_file=key_file,
66+
)
67+
68+
69+
@migrations_ns.command()
70+
def downgrade(
71+
ctx: typer.Context,
72+
filter_scripts: Optional[str] = typer.Option(
73+
None,
74+
help=FILTER_SCRIPTS_HELP,
75+
),
76+
key_file: Optional[Path] = typer.Option(
77+
None,
78+
help=KEY_FILE_HELP,
79+
),
80+
):
81+
cli_config = cast(CliConfig, ctx.obj)
82+
run_migration_command(
83+
cli_config=cli_config,
84+
command="downgrade",
85+
filter_scripts=filter_scripts,
86+
key_file=key_file,
87+
)

0 commit comments

Comments
 (0)