Files
2024-02-15 17:01:50 +00:00

111 lines
3.3 KiB
Python
Executable File

#!/usr/bin/python3
import json # noqa
import re
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any
import click
"""Convenience script for setting up silos in a dev environment.
Prints devserver commands with the necessary environment variables.
"""
@dataclass(frozen=True)
class RegionConfig:
number: int
port: int
api_token: str
@property
def name(self) -> str:
return f"devregion{self.number}"
@property
def bind(self) -> str:
return f"localhost:{self.port}"
def get_env_repr(self) -> Mapping[str, Any]:
return {
"name": self.name,
"id": self.number,
"address": f"http://{self.bind}/",
"category": "MULTI_TENANT",
"api_token": self.api_token,
}
def format_env_vars(env_vars: Mapping[str, str]) -> str:
def format_env_var(name: str, value: str) -> str:
value = re.sub("([\"$'\\\\])", "\\\\\\1", value) # https://xkcd.com/1638/
return f'{name}="{value}"'
return " ".join(format_env_var(name, value) for (name, value) in env_vars.items())
@click.command()
@click.option(
"--api-token",
type=str,
required=True,
help=(
"""An API token to authorize RPC requests between servers.
At the time this script is being written, the authentication system for RPCs
hasn't been developed. An API token is needed to ensure that an incoming
request hits a REST endpoint, but it doesn't need to have any particular
permission scope.
Because dev instances of the control and region silo will presumably share a
single database, this script assumes that the same API token will work for both.
"""
),
)
@click.option("--region-count", type=int, default=2, help="Number of region silos")
@click.option(
"--control-port",
type=int,
default=8000,
help=(
"""Port on which to bind the control silo.
Region silos will be bound on ascending numbers after this one in steps of 10.
"""
),
)
def main(api_token: str, region_count: int, control_port: int) -> None:
sender_credentials = {
"is_allowed": True,
"control_silo_api_token": api_token,
"control_silo_address": f"http://localhost:{control_port}/",
}
regions = [
RegionConfig(n, control_port + 10 * n, api_token) for n in range(1, region_count + 1)
]
region_config = json.dumps([r.get_env_repr() for r in regions])
common_env_vars = {}
common_env_vars["SENTRY_REGION_CONFIG"] = region_config
common_env_vars["SENTRY_DEV_HYBRID_CLOUD_RPC_SENDER"] = json.dumps(sender_credentials)
control_env_vars = common_env_vars.copy()
control_env_vars["SENTRY_SILO_MODE"] = "CONTROL"
control_env_vars["SENTRY_DEVSERVER_BIND"] = f"localhost:{control_port}"
print(f"# Control silo\n{format_env_vars(control_env_vars)} sentry devserver") # noqa
for region in regions:
region_env_vars = common_env_vars.copy()
region_env_vars["SENTRY_SILO_MODE"] = "REGION"
region_env_vars["SENTRY_REGION"] = region.name
region_env_vars["SENTRY_DEVSERVER_BIND"] = region.bind
print(f"\n# {region.name}\n{format_env_vars(region_env_vars)} sentry devserver") # noqa
main()