mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-11 10:56:14 +03:00
cmds: Implement chia beta
(#12389)
* cmds: Implement `chia beta` for the beta test program * Unhide and document all `beta` subcommands * Refactor all subcommands * Introduce `chia beta configure` * Introduce `chia beta status` * Test all `chia beta` commands * Use a separate file logger for beta logs * Write the plotting call args to the log file * Sort potential submissions * Some refactoring around log file log handler creation * JSON dump the plotting args
This commit is contained in:
parent
8aa0e7da90
commit
70fe981fb3
163
chia/cmds/beta.py
Normal file
163
chia/cmds/beta.py
Normal file
@ -0,0 +1,163 @@
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
import click
|
||||
|
||||
from chia.cmds.beta_funcs import (
|
||||
default_beta_root_path,
|
||||
prepare_chia_blockchain_log,
|
||||
prepare_logs,
|
||||
prepare_plotting_log,
|
||||
prompt_beta_warning,
|
||||
prompt_for_beta_path,
|
||||
update_beta_config,
|
||||
validate_beta_path,
|
||||
)
|
||||
from chia.util.config import lock_and_load_config, save_config
|
||||
|
||||
|
||||
def print_restart_warning() -> None:
|
||||
print("\nRestart the daemon and any running chia services for changes to take effect.")
|
||||
|
||||
|
||||
@click.group("beta", hidden=True)
|
||||
def beta_cmd() -> None:
|
||||
pass
|
||||
|
||||
|
||||
@beta_cmd.command("configure", help="Configure the beta test mode parameters")
|
||||
@click.option("-p", "--path", help="The beta mode root path", type=str, required=False)
|
||||
@click.pass_context
|
||||
def configure(ctx: click.Context, path: Optional[str]) -> None:
|
||||
root_path = ctx.obj["root_path"]
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
if "beta" not in config:
|
||||
ctx.exit("beta test mode is not enabled, enable it first with `chia beta enable`")
|
||||
|
||||
# Adjust the path
|
||||
if path is None:
|
||||
beta_root_path = prompt_for_beta_path(Path(config["beta"].get("path", default_beta_root_path())))
|
||||
else:
|
||||
beta_root_path = Path(path)
|
||||
validate_beta_path(beta_root_path)
|
||||
|
||||
update_beta_config(True, beta_root_path, config)
|
||||
save_config(root_path, "config.yaml", config)
|
||||
|
||||
print("\nbeta config updated")
|
||||
print_restart_warning()
|
||||
|
||||
|
||||
@beta_cmd.command("enable", help="Enable beta test mode")
|
||||
@click.option(
|
||||
"-f",
|
||||
"--force",
|
||||
help="Force accept the beta program warning",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
)
|
||||
@click.option("-p", "--path", help="The beta mode root path", type=str, required=False)
|
||||
@click.pass_context
|
||||
def enable_cmd(ctx: click.Context, force: bool, path: Optional[str]) -> None:
|
||||
root_path = ctx.obj["root_path"]
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
if config.get("beta", {}).get("enabled", False):
|
||||
ctx.exit("beta test mode is already enabled")
|
||||
|
||||
if not force and not prompt_beta_warning():
|
||||
ctx.abort()
|
||||
|
||||
# Use the existing beta path if there is one and no path was provided as parameter
|
||||
current_path = config.get("beta", {}).get("path")
|
||||
current_path = None if current_path is None else Path(current_path)
|
||||
|
||||
if path is None and current_path is None:
|
||||
beta_root_path = prompt_for_beta_path(current_path or default_beta_root_path())
|
||||
else:
|
||||
beta_root_path = Path(path or current_path)
|
||||
validate_beta_path(beta_root_path)
|
||||
|
||||
update_beta_config(True, beta_root_path, config)
|
||||
save_config(root_path, "config.yaml", config)
|
||||
|
||||
print(f"\nbeta test mode enabled with path {str(beta_root_path)!r}")
|
||||
print_restart_warning()
|
||||
|
||||
|
||||
@beta_cmd.command("disable", help="Disable beta test mode")
|
||||
@click.pass_context
|
||||
def disable_cmd(ctx: click.Context) -> None:
|
||||
root_path = ctx.obj["root_path"]
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
if not config.get("beta", {}).get("enabled", False):
|
||||
ctx.exit("beta test mode is not enabled")
|
||||
config["beta"]["enabled"] = False
|
||||
save_config(root_path, "config.yaml", config)
|
||||
|
||||
print("\nbeta test mode disabled")
|
||||
print_restart_warning()
|
||||
|
||||
|
||||
@beta_cmd.command("prepare_submission", help="Prepare the collected log data for submission")
|
||||
@click.pass_context
|
||||
def prepare_submission_cmd(ctx: click.Context) -> None:
|
||||
with lock_and_load_config(ctx.obj["root_path"], "config.yaml") as config:
|
||||
beta_root_path = config.get("beta", {}).get("path", None)
|
||||
if beta_root_path is None:
|
||||
ctx.exit("beta test mode not enabled. Run `chia beta enable` first.")
|
||||
beta_root_path = Path(beta_root_path)
|
||||
validate_beta_path(beta_root_path)
|
||||
available_results = sorted([path for path in beta_root_path.iterdir() if path.is_dir()])
|
||||
if len(available_results) == 0:
|
||||
ctx.exit(f"No beta logs found in {str(beta_root_path)!r}.")
|
||||
print("Available versions:")
|
||||
for i in range(len(available_results)):
|
||||
print(f" [{i + 1}] {available_results[i].name}")
|
||||
|
||||
user_input = input("Select the version you want to prepare for submission: ")
|
||||
try:
|
||||
if int(user_input) <= 0:
|
||||
raise IndexError()
|
||||
prepare_result = available_results[int(user_input) - 1]
|
||||
except IndexError:
|
||||
ctx.exit(f"Invalid choice: {user_input}")
|
||||
plotting_path = Path(prepare_result / "plotting")
|
||||
chia_blockchain_path = Path(prepare_result / "chia-blockchain")
|
||||
chia_logs = prepare_logs(plotting_path, prepare_chia_blockchain_log)
|
||||
plotting_logs = prepare_logs(chia_blockchain_path, prepare_plotting_log)
|
||||
|
||||
submission_file_path = (
|
||||
prepare_result / f"submission_{prepare_result.name}__{datetime.now().strftime('%m_%d_%Y__%H_%M_%S')}.zip"
|
||||
)
|
||||
|
||||
def add_files(paths: List[Path]) -> int:
|
||||
added = 0
|
||||
for path in paths:
|
||||
if path.name.startswith("."):
|
||||
continue
|
||||
zip_file.write(path, path.relative_to(prepare_result))
|
||||
added += 1
|
||||
return added
|
||||
|
||||
with zipfile.ZipFile(submission_file_path, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
||||
files_added = add_files(chia_logs) + add_files(plotting_logs)
|
||||
|
||||
if files_added == 0:
|
||||
submission_file_path.unlink()
|
||||
ctx.exit(f"No logs files found in {str(plotting_path)!r} and {str(chia_blockchain_path)!r}.")
|
||||
|
||||
print(f"\nDone. You can find the prepared submission data in {submission_file_path}.")
|
||||
|
||||
|
||||
@beta_cmd.command("status", help="Show the current beta configuration")
|
||||
@click.pass_context
|
||||
def status(ctx: click.Context) -> None:
|
||||
with lock_and_load_config(ctx.obj["root_path"], "config.yaml") as config:
|
||||
beta_config = config.get("beta")
|
||||
if beta_config is None:
|
||||
ctx.exit("beta test mode is not enabled, enable it first with `chia beta enable`")
|
||||
|
||||
print(f"enabled: {beta_config['enabled']}")
|
||||
print(f"path: {beta_config['path']}")
|
100
chia/cmds/beta_funcs.py
Normal file
100
chia/cmds/beta_funcs.py
Normal file
@ -0,0 +1,100 @@
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
from chia.util.chia_logging import get_beta_logging_config
|
||||
from chia.util.errors import InvalidPathError
|
||||
from chia.util.misc import format_bytes, prompt_yes_no, validate_directory_writable
|
||||
|
||||
|
||||
def default_beta_root_path() -> Path:
|
||||
return Path(os.path.expanduser(os.getenv("CHIA_BETA_ROOT", "~/chia-beta-test"))).resolve()
|
||||
|
||||
|
||||
def warn_if_beta_enabled(config: Dict[str, Any]) -> None:
|
||||
if config.get("beta", {}).get("enabled", False):
|
||||
print("\nWARNING: beta test mode is enabled. Run `chia beta disable` if this is unintentional.\n")
|
||||
|
||||
|
||||
def prompt_beta_warning() -> bool:
|
||||
logging_config = get_beta_logging_config()
|
||||
# The `/ 5` is just a rough estimation for `gzip` being used by the log rotation in beta mode. It was like
|
||||
# 7-10x compressed in example tests with 2MB files.
|
||||
min_space = format_bytes(int(logging_config["log_maxfilesrotation"] * logging_config["log_maxbytesrotation"] / 5))
|
||||
return prompt_yes_no(
|
||||
f"\nWARNING: Enabling the beta test mode increases disk writes and may lead to {min_space} of "
|
||||
"extra logfiles getting stored on your disk. This should only be done if you are part of the beta test "
|
||||
"program at: https://chia.net/beta-test\n\nDo you really want to enable the beta test mode?"
|
||||
)
|
||||
|
||||
|
||||
def prompt_for_beta_path(default_path: Path) -> Path:
|
||||
path: Optional[Path] = None
|
||||
for _ in range(3):
|
||||
user_input = input(
|
||||
"\nEnter a directory where the beta test logs can be stored or press enter to use the default "
|
||||
f"[{str(default_path)}]:"
|
||||
)
|
||||
test_path = Path(user_input) if user_input else default_path
|
||||
if not test_path.is_dir() and prompt_yes_no(
|
||||
f"\nDirectory {str(test_path)!r} doesn't exist.\n\nDo you want to create it?"
|
||||
):
|
||||
test_path.mkdir(parents=True)
|
||||
|
||||
try:
|
||||
validate_directory_writable(test_path)
|
||||
except InvalidPathError as e:
|
||||
print(str(e))
|
||||
continue
|
||||
|
||||
path = test_path
|
||||
break
|
||||
|
||||
if path is None:
|
||||
sys.exit("Aborted!")
|
||||
else:
|
||||
return path
|
||||
|
||||
|
||||
def update_beta_config(enabled: bool, path: Path, config: Dict[str, Any]) -> None:
|
||||
if "beta" not in config:
|
||||
config["beta"] = {}
|
||||
|
||||
config["beta"].update(
|
||||
{
|
||||
"enabled": enabled,
|
||||
"path": str(path),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_beta_path(beta_root_path: Path) -> None:
|
||||
try:
|
||||
validate_directory_writable(beta_root_path)
|
||||
except InvalidPathError as e:
|
||||
sys.exit(str(e))
|
||||
|
||||
|
||||
def prepare_plotting_log(path: Path) -> None:
|
||||
# TODO: Do stuff we want to do with the logs before submission. Maybe even just fully parse them and
|
||||
# create some final result files and zip them instead of just the logs.
|
||||
print(f" - {path.name}")
|
||||
|
||||
|
||||
def prepare_chia_blockchain_log(path: Path) -> None:
|
||||
# TODO: Do stuff we want to do with the logs before submission. Maybe even just fully parse them and
|
||||
# create some final result files and zip them instead of just the logs.
|
||||
print(f" - {path.name}")
|
||||
|
||||
|
||||
def prepare_logs(prepare_path: Path, prepare_callback: Callable[[Path], None]) -> List[Path]:
|
||||
result = [path for path in prepare_path.iterdir()] if prepare_path.exists() else []
|
||||
if len(result):
|
||||
print(f"\nPreparing {prepare_path.name!r} logs:")
|
||||
for log in result:
|
||||
if log.name.startswith("."):
|
||||
continue
|
||||
prepare_callback(log)
|
||||
|
||||
return result
|
@ -2,6 +2,7 @@ from io import TextIOWrapper
|
||||
import click
|
||||
|
||||
from chia import __version__
|
||||
from chia.cmds.beta import beta_cmd
|
||||
from chia.cmds.configure import configure_cmd
|
||||
from chia.cmds.farm import farm_cmd
|
||||
from chia.cmds.data import data_cmd
|
||||
@ -144,6 +145,7 @@ cli.add_command(db_cmd)
|
||||
cli.add_command(peer_cmd)
|
||||
cli.add_command(data_cmd)
|
||||
cli.add_command(passphrase_cmd)
|
||||
cli.add_command(beta_cmd)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
@ -10,8 +10,10 @@ from chia.util.service_groups import all_groups
|
||||
@click.pass_context
|
||||
def start_cmd(ctx: click.Context, restart: bool, group: str) -> None:
|
||||
import asyncio
|
||||
from chia.cmds.beta_funcs import warn_if_beta_enabled
|
||||
from .start_funcs import async_start
|
||||
|
||||
root_path = ctx.obj["root_path"]
|
||||
config = load_config(root_path, "config.yaml")
|
||||
warn_if_beta_enabled(config)
|
||||
asyncio.run(async_start(root_path, config, group, restart, ctx.obj["force_legacy_keyring_migration"]))
|
||||
|
@ -50,6 +50,10 @@ async def async_stop(root_path: Path, config: Dict[str, Any], group: str, stop_d
|
||||
@click.argument("group", type=click.Choice(list(all_groups())), nargs=-1, required=True)
|
||||
@click.pass_context
|
||||
def stop_cmd(ctx: click.Context, daemon: bool, group: str) -> None:
|
||||
from chia.cmds.beta_funcs import warn_if_beta_enabled
|
||||
|
||||
root_path = ctx.obj["root_path"]
|
||||
config = load_config(root_path, "config.yaml")
|
||||
warn_if_beta_enabled(config)
|
||||
|
||||
sys.exit(asyncio.run(async_stop(root_path, config, group, daemon)))
|
||||
|
@ -23,7 +23,7 @@ from chia.plotters.plotters import get_available_plotters
|
||||
from chia.plotting.util import add_plot_directory
|
||||
from chia.server.server import ssl_context_for_root, ssl_context_for_server
|
||||
from chia.ssl.create_ssl import get_mozilla_ca_crt
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config
|
||||
from chia.util.errors import KeychainRequiresMigration, KeychainCurrentPassphraseIsInvalid
|
||||
from chia.util.json_util import dict_to_json_str
|
||||
@ -1331,7 +1331,7 @@ async def async_run_daemon(root_path: Path, wait_for_unlock: bool = False) -> in
|
||||
chia_init(root_path, should_check_keys=(not wait_for_unlock))
|
||||
config = load_config(root_path, "config.yaml")
|
||||
setproctitle("chia_daemon")
|
||||
initialize_logging("daemon", config["logging"], root_path)
|
||||
initialize_service_logging("daemon", config)
|
||||
crt_path = root_path / config["daemon_ssl"]["private_crt"]
|
||||
key_path = root_path / config["daemon_ssl"]["private_key"]
|
||||
ca_crt_path = root_path / config["private_ssl_ca"]["crt"]
|
||||
|
@ -219,7 +219,7 @@ def plot_bladebit(args, chia_root_path, root_path):
|
||||
call_args.append("-m")
|
||||
call_args.append(args.finaldir)
|
||||
try:
|
||||
asyncio.run(run_plotter(call_args, progress))
|
||||
asyncio.run(run_plotter(chia_root_path, args.plotter, call_args, progress))
|
||||
except Exception as e:
|
||||
print(f"Exception while plotting: {e} {type(e)}")
|
||||
print(f"Traceback: {traceback.format_exc()}")
|
||||
|
@ -236,7 +236,7 @@ def plot_madmax(args, chia_root_path: Path, plotters_root_path: Path):
|
||||
call_args.append("-k")
|
||||
call_args.append(str(args.size))
|
||||
try:
|
||||
asyncio.run(run_plotter(call_args, progress))
|
||||
asyncio.run(run_plotter(chia_root_path, args.plotter, call_args, progress))
|
||||
except Exception as e:
|
||||
print(f"Exception while plotting: {type(e)} {e}")
|
||||
print(f"Traceback: {traceback.format_exc()}")
|
||||
|
@ -1,7 +1,30 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import json
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Iterator, Optional, TextIO
|
||||
|
||||
from chia.cmds.init_funcs import chia_full_version_str
|
||||
from chia.util.config import lock_and_load_config
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def get_optional_beta_plot_log_file(root_path: Path, plotter: str) -> Iterator[Optional[TextIO]]:
|
||||
beta_log_path: Optional[Path] = None
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
if "beta" in config:
|
||||
file_name = f"{plotter}_{datetime.now().strftime('%m_%d_%Y__%H_%M_%S')}.log"
|
||||
beta_log_path = Path(config["beta"]["path"]) / chia_full_version_str() / "plotting" / file_name
|
||||
beta_log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if beta_log_path is not None:
|
||||
with open(beta_log_path, "w") as file:
|
||||
yield file
|
||||
else:
|
||||
yield None
|
||||
|
||||
|
||||
# https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/
|
||||
@ -22,7 +45,7 @@ def parse_stdout(out, progress):
|
||||
print(f"Progress update: {v}", flush=True)
|
||||
|
||||
|
||||
async def run_plotter(args, progress_dict):
|
||||
async def run_plotter(root_path, plotter, args, progress_dict):
|
||||
orig_sigint_handler = signal.getsignal(signal.SIGINT)
|
||||
installed_sigint_handler = False
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
@ -37,27 +60,43 @@ async def run_plotter(args, progress_dict):
|
||||
signal.signal(signal.SIGINT, sigint_handler)
|
||||
installed_sigint_handler = True
|
||||
|
||||
try:
|
||||
await asyncio.wait(
|
||||
[
|
||||
_read_stream(
|
||||
process.stdout,
|
||||
lambda x: parse_stdout(x.decode("UTF8"), progress_dict),
|
||||
),
|
||||
_read_stream(
|
||||
process.stderr,
|
||||
lambda x: print("STDERR: {}".format(x.decode("UTF8"))),
|
||||
),
|
||||
]
|
||||
)
|
||||
with get_optional_beta_plot_log_file(root_path, plotter) as log_file:
|
||||
if log_file is not None:
|
||||
log_file.write(json.dumps(args) + "\n")
|
||||
|
||||
await process.wait()
|
||||
except Exception as e:
|
||||
print(f"Caught exception while invoking plotter: {e}")
|
||||
finally:
|
||||
# Restore the original SIGINT handler
|
||||
if installed_sigint_handler:
|
||||
signal.signal(signal.SIGINT, orig_sigint_handler)
|
||||
def process_stdout_line(line_bytes: bytes) -> None:
|
||||
line_str = line_bytes.decode("UTF8")
|
||||
parse_stdout(line_str, progress_dict)
|
||||
if log_file is not None:
|
||||
log_file.write(line_str)
|
||||
|
||||
def process_stderr_line(line_bytes: bytes) -> None:
|
||||
err_str = f"STDERR: {line_bytes.decode('UTF8')}"
|
||||
print(err_str)
|
||||
if log_file is not None:
|
||||
log_file.write(err_str)
|
||||
|
||||
try:
|
||||
await asyncio.wait(
|
||||
[
|
||||
_read_stream(
|
||||
process.stdout,
|
||||
process_stdout_line,
|
||||
),
|
||||
_read_stream(
|
||||
process.stderr,
|
||||
process_stderr_line,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
await process.wait()
|
||||
except Exception as e:
|
||||
print(f"Caught exception while invoking plotter: {e}")
|
||||
finally:
|
||||
# Restore the original SIGINT handler
|
||||
if installed_sigint_handler:
|
||||
signal.signal(signal.SIGINT, orig_sigint_handler)
|
||||
|
||||
|
||||
def run_command(args, exc_description, *, check=True, **kwargs) -> subprocess.CompletedProcess:
|
||||
|
@ -11,7 +11,7 @@ from chia.seeder.crawler import Crawler
|
||||
from chia.seeder.crawler_api import CrawlerAPI
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.start_service import RpcInfo, Service, async_run
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config, load_config_cli
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
|
||||
@ -67,11 +67,7 @@ async def async_main() -> int:
|
||||
config[SERVICE_NAME] = service_config
|
||||
overrides = service_config["network_overrides"]["constants"][service_config["selected_network"]]
|
||||
updated_constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
service = create_full_node_crawler_service(DEFAULT_ROOT_PATH, config, updated_constants)
|
||||
await service.setup_process_global_state()
|
||||
await service.run()
|
||||
|
@ -10,7 +10,7 @@ from chia.rpc.farmer_rpc_api import FarmerRpcApi
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.start_service import RpcInfo, Service, async_run
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config, load_config_cli
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
from chia.util.keychain import Keychain
|
||||
@ -72,11 +72,7 @@ async def async_main() -> int:
|
||||
config[SERVICE_NAME] = service_config
|
||||
config_pool = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", "pool")
|
||||
config["pool"] = config_pool
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
service = create_farmer_service(DEFAULT_ROOT_PATH, config, config_pool, DEFAULT_CONSTANTS)
|
||||
await service.setup_process_global_state()
|
||||
await service.run()
|
||||
|
@ -12,7 +12,7 @@ from chia.full_node.full_node_api import FullNodeAPI
|
||||
from chia.rpc.full_node_rpc_api import FullNodeRpcApi
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.start_service import RpcInfo, Service, async_run
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config, load_config_cli
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
from chia.util.ints import uint16
|
||||
@ -72,11 +72,7 @@ async def async_main() -> int:
|
||||
config[SERVICE_NAME] = service_config
|
||||
overrides = service_config["network_overrides"]["constants"][service_config["selected_network"]]
|
||||
updated_constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
service = create_full_node_service(DEFAULT_ROOT_PATH, config, updated_constants)
|
||||
await service.setup_process_global_state()
|
||||
await service.run()
|
||||
|
@ -10,7 +10,7 @@ from chia.rpc.harvester_rpc_api import HarvesterRpcApi
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.start_service import RpcInfo, Service, async_run
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config, load_config_cli
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
|
||||
@ -60,11 +60,7 @@ async def async_main() -> int:
|
||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||
service_config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME)
|
||||
config[SERVICE_NAME] = service_config
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
farmer_peer = PeerInfo(service_config["farmer_peer"]["host"], service_config["farmer_peer"]["port"])
|
||||
service = create_harvester_service(DEFAULT_ROOT_PATH, config, DEFAULT_CONSTANTS, farmer_peer)
|
||||
await service.setup_process_global_state()
|
||||
|
@ -6,7 +6,7 @@ from chia.introducer.introducer import Introducer
|
||||
from chia.introducer.introducer_api import IntroducerAPI
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.start_service import Service, async_run
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config, load_config_cli
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
|
||||
@ -50,11 +50,7 @@ async def async_main() -> int:
|
||||
service_config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME)
|
||||
config[SERVICE_NAME] = service_config
|
||||
service = create_introducer_service(DEFAULT_ROOT_PATH, config)
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
await service.setup_process_global_state()
|
||||
await service.run()
|
||||
|
||||
|
@ -11,7 +11,7 @@ from chia.server.start_service import RpcInfo, Service, async_run
|
||||
from chia.timelord.timelord import Timelord
|
||||
from chia.timelord.timelord_api import TimelordAPI
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config, load_config_cli
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
|
||||
@ -66,11 +66,7 @@ async def async_main() -> int:
|
||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||
service_config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME)
|
||||
config[SERVICE_NAME] = service_config
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
service = create_timelord_service(DEFAULT_ROOT_PATH, config, DEFAULT_CONSTANTS)
|
||||
await service.setup_process_global_state()
|
||||
await service.run()
|
||||
|
@ -10,7 +10,7 @@ from chia.rpc.wallet_rpc_api import WalletRpcApi
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.start_service import RpcInfo, Service, async_run
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.util.chia_logging import initialize_logging
|
||||
from chia.util.chia_logging import initialize_service_logging
|
||||
from chia.util.config import load_config_cli, load_config
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
from chia.util.keychain import Keychain
|
||||
@ -99,11 +99,7 @@ async def async_main() -> int:
|
||||
service_config["selected_network"] = "testnet0"
|
||||
else:
|
||||
constants = DEFAULT_CONSTANTS
|
||||
initialize_logging(
|
||||
service_name=SERVICE_NAME,
|
||||
logging_config=service_config["logging"],
|
||||
root_path=DEFAULT_ROOT_PATH,
|
||||
)
|
||||
initialize_service_logging(service_name=SERVICE_NAME, config=config)
|
||||
service = create_wallet_service(DEFAULT_ROOT_PATH, config, constants)
|
||||
await service.setup_process_global_state()
|
||||
await service.run()
|
||||
|
@ -1,20 +1,49 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import colorlog
|
||||
from concurrent_log_handler import ConcurrentRotatingFileHandler
|
||||
from logging.handlers import SysLogHandler
|
||||
|
||||
from chia.cmds.init_funcs import chia_full_version_str
|
||||
from chia.util.path import path_from_root
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
|
||||
|
||||
def initialize_logging(service_name: str, logging_config: Dict, root_path: Path):
|
||||
log_path = path_from_root(root_path, logging_config.get("log_filename", "log/debug.log"))
|
||||
log_date_format = "%Y-%m-%dT%H:%M:%S"
|
||||
def get_beta_logging_config() -> Dict[str, Any]:
|
||||
return {
|
||||
"log_filename": f"{chia_full_version_str()}/chia-blockchain/beta.log",
|
||||
"log_level": "DEBUG",
|
||||
"log_stdout": False,
|
||||
"log_maxfilesrotation": 100,
|
||||
"log_maxbytesrotation": 100 * 1024 * 1024,
|
||||
"log_use_gzip": True,
|
||||
}
|
||||
|
||||
|
||||
def get_file_log_handler(
|
||||
formatter: logging.Formatter, root_path: Path, logging_config: Dict[str, object]
|
||||
) -> ConcurrentRotatingFileHandler:
|
||||
log_path = path_from_root(root_path, str(logging_config.get("log_filename", "log/debug.log")))
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
maxrotation = logging_config.get("log_maxfilesrotation", 7)
|
||||
maxbytesrotation = logging_config.get("log_maxbytesrotation", 50 * 1024 * 1024)
|
||||
use_gzip = logging_config.get("log_use_gzip", False)
|
||||
handler = ConcurrentRotatingFileHandler(
|
||||
log_path, "a", maxBytes=maxbytesrotation, backupCount=maxrotation, use_gzip=use_gzip
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
return handler
|
||||
|
||||
|
||||
def initialize_logging(service_name: str, logging_config: Dict, root_path: Path, beta_root_path: Optional[Path] = None):
|
||||
file_name_length = 33 - len(service_name)
|
||||
log_date_format = "%Y-%m-%dT%H:%M:%S"
|
||||
file_log_formatter = logging.Formatter(
|
||||
fmt=f"%(asctime)s.%(msecs)03d {service_name} %(name)-{file_name_length}s: %(levelname)-8s %(message)s",
|
||||
datefmt=log_date_format,
|
||||
)
|
||||
if logging_config["log_stdout"]:
|
||||
handler = colorlog.StreamHandler()
|
||||
handler.setFormatter(
|
||||
@ -30,16 +59,7 @@ def initialize_logging(service_name: str, logging_config: Dict, root_path: Path)
|
||||
logger.addHandler(handler)
|
||||
else:
|
||||
logger = logging.getLogger()
|
||||
maxrotation = logging_config.get("log_maxfilesrotation", 7)
|
||||
maxbytesrotation = logging_config.get("log_maxbytesrotation", 50 * 1024 * 1024)
|
||||
handler = ConcurrentRotatingFileHandler(log_path, "a", maxBytes=maxbytesrotation, backupCount=maxrotation)
|
||||
handler.setFormatter(
|
||||
logging.Formatter(
|
||||
fmt=f"%(asctime)s.%(msecs)03d {service_name} %(name)-{file_name_length}s: %(levelname)-8s %(message)s",
|
||||
datefmt=log_date_format,
|
||||
)
|
||||
)
|
||||
logger.addHandler(handler)
|
||||
logger.addHandler(get_file_log_handler(file_log_formatter, root_path, logging_config))
|
||||
|
||||
if logging_config.get("log_syslog", False):
|
||||
log_syslog_host = logging_config.get("log_syslog_host", "localhost")
|
||||
@ -65,3 +85,24 @@ def initialize_logging(service_name: str, logging_config: Dict, root_path: Path)
|
||||
logger.setLevel(logging.INFO)
|
||||
else:
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
if beta_root_path is not None:
|
||||
logger.addHandler(get_file_log_handler(file_log_formatter, beta_root_path, get_beta_logging_config()))
|
||||
|
||||
|
||||
def initialize_service_logging(service_name: str, config: Dict[str, Any]) -> None:
|
||||
logging_root_path = DEFAULT_ROOT_PATH
|
||||
if service_name == "daemon":
|
||||
# TODO: Maybe introduce a separate `daemon` section in the config instead of having `daemon_port`, `logging`
|
||||
# and the daemon related stuff as top level entries.
|
||||
logging_config = config["logging"]
|
||||
else:
|
||||
logging_config = config[service_name]["logging"]
|
||||
beta_config = config.get("beta", {})
|
||||
beta_config_path = beta_config.get("path") if beta_config.get("enabled", False) else None
|
||||
initialize_logging(
|
||||
service_name=service_name,
|
||||
logging_config=logging_config,
|
||||
root_path=logging_root_path,
|
||||
beta_root_path=beta_config_path,
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, List
|
||||
|
||||
|
||||
@ -281,3 +282,14 @@ class KeychainLabelExists(KeychainLabelError):
|
||||
def __init__(self, label: str, fingerprint: int) -> None:
|
||||
super().__init__(label, f"label {label!r} already exists for fingerprint {str(fingerprint)!r}")
|
||||
self.fingerprint = fingerprint
|
||||
|
||||
|
||||
##
|
||||
# Miscellaneous errors
|
||||
##
|
||||
|
||||
|
||||
class InvalidPathError(Exception):
|
||||
def __init__(self, path: Path, error_message: str):
|
||||
super().__init__(f"{error_message}: {str(path)!r}")
|
||||
self.path = path
|
||||
|
@ -119,6 +119,7 @@ logging: &logging
|
||||
log_level: "WARNING" # Can be CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET
|
||||
log_maxfilesrotation: 7 # Max files in rotation. Default value 7 if the key is not set
|
||||
log_maxbytesrotation: 52428800 # Max bytes logged before rotating logs
|
||||
log_use_gzip: False # Use gzip to compress rotated logs
|
||||
log_syslog: False # If True, outputs to SysLog host and port specified
|
||||
log_syslog_host: "localhost" # Send logging messages to a remote or local Unix syslog
|
||||
log_syslog_port: 514 # UDP port of the remote or local Unix syslog
|
||||
|
@ -1,6 +1,8 @@
|
||||
import dataclasses
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Sequence, Union
|
||||
|
||||
from chia.util.errors import InvalidPathError
|
||||
from chia.util.ints import uint16
|
||||
from chia.util.streamable import Streamable, recurse_jsonify, streamable
|
||||
|
||||
@ -91,3 +93,15 @@ def get_list_or_len(list_in: Sequence[object], length: bool) -> Union[int, Seque
|
||||
def dataclass_to_json_dict(instance: Any) -> Dict[str, Any]:
|
||||
ret: Dict[str, Any] = recurse_jsonify(instance)
|
||||
return ret
|
||||
|
||||
|
||||
def validate_directory_writable(path: Path) -> None:
|
||||
write_test_path = path / ".write_test"
|
||||
try:
|
||||
with write_test_path.open("w"):
|
||||
pass
|
||||
write_test_path.unlink()
|
||||
except FileNotFoundError:
|
||||
raise InvalidPathError(path, "Directory doesn't exist")
|
||||
except OSError:
|
||||
raise InvalidPathError(path, "Directory not writable")
|
||||
|
335
tests/core/cmds/test_beta.py
Normal file
335
tests/core/cmds/test_beta.py
Normal file
@ -0,0 +1,335 @@
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Callable, Optional
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner, Result
|
||||
|
||||
from chia.cmds.beta_funcs import default_beta_root_path
|
||||
from chia.cmds.chia import cli
|
||||
from chia.util.config import lock_and_load_config, save_config
|
||||
|
||||
|
||||
def configure(root_path: Path, *args: str) -> Result:
|
||||
return CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"configure",
|
||||
*args,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def configure_interactive(root_path: Path, user_input: Optional[str] = None) -> Result:
|
||||
return CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"configure",
|
||||
],
|
||||
input=user_input,
|
||||
)
|
||||
|
||||
|
||||
def enable(root_path: Path, *args: str) -> Result:
|
||||
return CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"enable",
|
||||
"--force",
|
||||
*args,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def enable_interactive(root_path: Path, user_input: Optional[str] = None) -> Result:
|
||||
return CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"enable",
|
||||
],
|
||||
input=user_input,
|
||||
)
|
||||
|
||||
|
||||
def prepare_submission(root_path: Path, user_input: Optional[str] = None) -> Result:
|
||||
return CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"prepare_submission",
|
||||
],
|
||||
input=user_input,
|
||||
)
|
||||
|
||||
|
||||
def generate_example_submission_data(beta_root_path: Path, versions: int, logs: int) -> None:
|
||||
for version in range(versions):
|
||||
version_path = beta_root_path / str(version)
|
||||
version_path.mkdir()
|
||||
chia_blockchain_logs = version_path / "chia-blockchain"
|
||||
plotting_logs = version_path / "plotting"
|
||||
chia_blockchain_logs.mkdir()
|
||||
plotting_logs.mkdir()
|
||||
for i in range(logs):
|
||||
with open(chia_blockchain_logs / f"beta_{i}.log", "w"):
|
||||
pass
|
||||
with open(chia_blockchain_logs / f"beta_{i + 10}.gz", "w"):
|
||||
pass
|
||||
with open(plotting_logs / f"plot_{i}.log", "w"):
|
||||
pass
|
||||
|
||||
|
||||
def generate_beta_config(root_path: Path, enabled: bool, beta_path: Path) -> None:
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
config["beta"] = {
|
||||
"enabled": enabled,
|
||||
"path": str(beta_path),
|
||||
}
|
||||
save_config(root_path, "config.yaml", config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option", ["--path", "-p"])
|
||||
def test_configure(root_path_populated_with_config: Path, option: str) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
beta_path.mkdir()
|
||||
generate_beta_config(root_path, True, beta_path)
|
||||
|
||||
result = configure(root_path, option, str(beta_path))
|
||||
assert result.exit_code == 0
|
||||
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert config["beta"] == {
|
||||
"enabled": True,
|
||||
"path": str(beta_path),
|
||||
}
|
||||
|
||||
|
||||
def test_configure_no_beta_config(root_path_populated_with_config: Path) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
beta_path.mkdir()
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert "beta" not in config
|
||||
|
||||
result = configure(root_path, "--path", str(beta_path))
|
||||
assert result.exit_code == 1
|
||||
assert "beta test mode is not enabled, enable it first with `chia beta enable`" in result.output
|
||||
|
||||
|
||||
@pytest.mark.parametrize("accept_existing_path", [True, False])
|
||||
def test_beta_configure_interactive(root_path_populated_with_config: Path, accept_existing_path: bool) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
generate_beta_config(root_path, True, root_path_populated_with_config)
|
||||
|
||||
result = configure_interactive(root_path, f"{'' if accept_existing_path else str(beta_path)}\ny\n")
|
||||
assert result.exit_code == 0
|
||||
assert "beta config updated" in result.output
|
||||
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert config["beta"] == {
|
||||
"enabled": True,
|
||||
"path": str(root_path_populated_with_config if accept_existing_path else beta_path),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option", ["--path", "-p"])
|
||||
def test_beta_enable(root_path_populated_with_config: Path, option: str) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
beta_path.mkdir()
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert "beta" not in config
|
||||
|
||||
result = enable(root_path, option, str(beta_path))
|
||||
assert result.exit_code == 0
|
||||
assert f"beta test mode enabled with path {str(beta_path)!r}" in result.output
|
||||
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert config["beta"] == {
|
||||
"enabled": True,
|
||||
"path": str(beta_path),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enabled", [True, False])
|
||||
def test_beta_enable_preconfigured(root_path_populated_with_config: Path, enabled: bool) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
beta_path.mkdir()
|
||||
generate_beta_config(root_path, enabled, beta_path)
|
||||
|
||||
result = enable_interactive(root_path, "y\n")
|
||||
|
||||
if enabled:
|
||||
assert result.exit_code == 1
|
||||
assert "beta test mode is already enabled" in result.output
|
||||
else:
|
||||
assert result.exit_code == 0
|
||||
assert f"beta test mode enabled with path {str(beta_path)!r}" in result.output
|
||||
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert config["beta"] == {
|
||||
"enabled": True,
|
||||
"path": str(beta_path),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("accept_default_path", [True, False])
|
||||
def test_beta_enable_interactive(root_path_populated_with_config: Path, accept_default_path: bool) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert "beta" not in config
|
||||
|
||||
result = enable_interactive(root_path, f"y\n{'' if accept_default_path else str(beta_path)}\ny\n")
|
||||
assert result.exit_code == 0
|
||||
assert (
|
||||
f"beta test mode enabled with path {str(default_beta_root_path() if accept_default_path else beta_path)!r}"
|
||||
in result.output
|
||||
)
|
||||
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert config["beta"] == {
|
||||
"enabled": True,
|
||||
"path": str(default_beta_root_path() if accept_default_path else beta_path),
|
||||
}
|
||||
|
||||
|
||||
def test_beta_enable_interactive_decline_warning(root_path_populated_with_config: Path) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert "beta" not in config
|
||||
|
||||
result = enable_interactive(root_path, "n\n")
|
||||
assert result.exit_code == 1
|
||||
assert result.output[-9:-1] == "Aborted!"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("write_test", [True, False])
|
||||
@pytest.mark.parametrize("command", [configure, enable])
|
||||
def test_beta_invalid_directories(
|
||||
root_path_populated_with_config: Path, write_test: bool, command: Callable[[Path, str, str], Result]
|
||||
) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
if write_test:
|
||||
(beta_path / ".write_test").mkdir(parents=True) # `.write_test` is used in validate_directory_writable
|
||||
if command == configure:
|
||||
generate_beta_config(root_path, True, root_path_populated_with_config)
|
||||
result = command(root_path, "--path", str(beta_path))
|
||||
assert result.exit_code == 1
|
||||
if write_test:
|
||||
assert f"Directory not writable: {str(beta_path)!r}" in result.output
|
||||
else:
|
||||
assert f"Directory doesn't exist: {str(beta_path)!r}" in result.output
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enabled", [True, False])
|
||||
def test_beta_disable(root_path_populated_with_config: Path, enabled: bool) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
generate_beta_config(root_path, enabled, beta_path)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"disable",
|
||||
],
|
||||
)
|
||||
if enabled:
|
||||
assert result.exit_code == 0
|
||||
assert "beta test mode disabled" in result.output
|
||||
else:
|
||||
assert result.exit_code == 1
|
||||
assert "beta test mode is not enabled" in result.output
|
||||
|
||||
with lock_and_load_config(root_path, "config.yaml") as config:
|
||||
assert config["beta"] == {
|
||||
"enabled": False,
|
||||
"path": str(beta_path),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"versions, logs, choice, exit_code, output",
|
||||
[
|
||||
(0, 0, 1, 1, "No beta logs found"),
|
||||
(1, 0, 1, 1, "No logs files found"),
|
||||
(2, 10, 3, 1, "Invalid choice: 3"),
|
||||
(2, 10, 0, 1, "Invalid choice: 0"),
|
||||
(2, 10, -1, 1, "Invalid choice: -1"),
|
||||
(4, 3, 2, 0, "Done. You can find the prepared submission data"),
|
||||
],
|
||||
)
|
||||
def test_prepare_submission(
|
||||
root_path_populated_with_config: Path, versions: int, logs: int, choice: int, exit_code: int, output: str
|
||||
) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
beta_path = root_path / "beta"
|
||||
beta_path.mkdir()
|
||||
generate_beta_config(root_path, True, beta_path)
|
||||
|
||||
generate_example_submission_data(beta_path, versions, logs)
|
||||
|
||||
result = prepare_submission(root_path, f"{choice}\n")
|
||||
|
||||
assert result.exit_code == exit_code
|
||||
assert output in result.output
|
||||
|
||||
if exit_code == 0:
|
||||
submission_file = list(beta_path.rglob("*.zip"))[0]
|
||||
assert submission_file.name.startswith(f"submission_{choice - 1}")
|
||||
with zipfile.ZipFile(submission_file) as zip_file:
|
||||
all_files = [Path(info.filename) for info in zip_file.filelist]
|
||||
for version in range(versions):
|
||||
chia_blockchain_logs = Path("chia-blockchain")
|
||||
plotting_logs = Path("plotting")
|
||||
for i in range(logs):
|
||||
assert chia_blockchain_logs / f"beta_{i}.log" in all_files
|
||||
assert chia_blockchain_logs / f"beta_{i + 10}.gz" in all_files
|
||||
assert plotting_logs / f"plot_{i}.log" in all_files
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"enabled, path",
|
||||
[
|
||||
(True, Path("path_1")),
|
||||
(False, Path("path_2")),
|
||||
],
|
||||
)
|
||||
def test_beta_status(root_path_populated_with_config: Path, enabled: bool, path: Path) -> None:
|
||||
root_path = root_path_populated_with_config
|
||||
generate_beta_config(root_path, enabled, path)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"--root-path",
|
||||
str(root_path),
|
||||
"beta",
|
||||
"status",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert f"enabled: {enabled}" in result.output
|
||||
assert f"path: {str(path)}" in result.output
|
@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
from chia.util.misc import format_bytes
|
||||
from chia.util.errors import InvalidPathError
|
||||
from chia.util.misc import format_bytes, validate_directory_writable
|
||||
from chia.util.misc import format_minutes
|
||||
|
||||
|
||||
@ -50,3 +51,20 @@ class TestMisc:
|
||||
assert format_minutes(525600) == "1 year"
|
||||
assert format_minutes(1007400) == "1 year and 11 months"
|
||||
assert format_minutes(5256000) == "10 years"
|
||||
|
||||
|
||||
def test_validate_directory_writable(tmp_path) -> None:
|
||||
write_test_path = tmp_path / ".write_test" # `.write_test` is used in validate_directory_writable
|
||||
validate_directory_writable(tmp_path)
|
||||
assert not write_test_path.exists()
|
||||
|
||||
subdir = tmp_path / "subdir"
|
||||
with pytest.raises(InvalidPathError, match="Directory doesn't exist") as exc_info:
|
||||
validate_directory_writable(subdir)
|
||||
assert exc_info.value.path == subdir
|
||||
assert not write_test_path.exists()
|
||||
|
||||
(tmp_path / ".write_test").mkdir()
|
||||
with pytest.raises(InvalidPathError, match="Directory not writable") as exc_info:
|
||||
validate_directory_writable(tmp_path)
|
||||
assert exc_info.value.path == tmp_path
|
||||
|
Loading…
Reference in New Issue
Block a user