Source code for flux_restful.db.init_db
"""
Database setup: create the tables and manage users.
Used by the `flux-restful` command (init, add-user, list-users) and runnable
directly with `python3 -m flux_restful.db.init_db`.
"""
import argparse
import logging
import sys
import flux_restful.crud.user as crud_user
import flux_restful.schemas as schemas
from flux_restful.core.config import settings
# Import all models so SQLAlchemy knows every table before create_all
from flux_restful.db.base import Base
from flux_restful.db.session import SessionLocal, engine
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("flux-restful")
[docs]def create_tables() -> None:
"""
Create any missing tables (a no-op for existing ones).
"""
Base.metadata.create_all(bind=engine)
[docs]def init_db() -> None:
"""
Create the tables and the superuser from FLUX_USER / FLUX_TOKEN.
"""
if not settings.flux_user or not settings.flux_token:
sys.exit("Please export FLUX_USER and FLUX_TOKEN to create the superuser.")
create_tables()
logger.info("Creating initial data")
add_user(settings.flux_user, settings.flux_token, superuser=True)
logger.info("Initial data created")
[docs]def list_users():
"""
List users in the database.
"""
create_tables()
db = SessionLocal()
users = crud_user.get_multi(db)
for user in users:
flags = []
if user.is_superuser:
flags.append("superuser")
if not user.is_active:
flags.append("inactive")
logger.info("%s %s", user.user_name, " ".join(flags))
return users
[docs]def add_user(username, password, superuser=False, is_active=True) -> None:
"""
Add a user to the database (no-op if the username exists).
"""
username = username.strip()
password = password.strip()
create_tables()
db = SessionLocal()
user = crud_user.get_by_username(db, user_name=username)
if user:
logger.info(f"User {username} already exists.")
return
user_in = schemas.UserCreate(
user_name=username,
password=password,
is_superuser=superuser,
is_active=is_active,
)
try:
crud_user.create(db, obj_in=user_in)
except ValueError as e:
# e.g., a password longer than bcrypt's 72 byte limit
sys.exit(f"Cannot create user {username}: {e}")
logger.info(f"User {username} has been created.")
[docs]def main(argv=None) -> None:
parser = get_parser()
# If an error occurs while parsing the arguments, the interpreter will exit with value 2
args, _ = parser.parse_known_args(argv)
if args.command == "init":
init_db()
elif args.command == "list-users":
list_users()
elif args.command == "add-user":
add_user(args.username, args.password, superuser=args.superuser)
else:
sys.exit(f"{args.command} is not recognized.")
[docs]def get_parser():
parser = argparse.ArgumentParser(
description="Flux Restful Database",
formatter_class=argparse.RawTextHelpFormatter,
)
subparsers = parser.add_subparsers(
help="actions",
title="actions",
description="actions",
dest="command",
)
subparsers.add_parser(
"list-users",
description="list existing users",
formatter_class=argparse.RawTextHelpFormatter,
)
add_user = subparsers.add_parser(
"add-user",
description="add a new user and password to the database",
formatter_class=argparse.RawTextHelpFormatter,
)
add_user.add_argument("username", help="username")
add_user.add_argument("password", help="password")
add_user.add_argument(
"--superuser", action="store_true", help="make the user a superuser"
)
subparsers.add_parser(
"init",
description="create the tables and the FLUX_USER superuser",
formatter_class=argparse.RawTextHelpFormatter,
)
return parser
if __name__ == "__main__":
main()