Skip to content

Commit 9760c23

Browse files
committed
Initial pbench user model implementation
1 parent abab42d commit 9760c23

File tree

13 files changed

+1318
-8
lines changed

13 files changed

+1318
-8
lines changed

lib/pbench/cli/server/shell.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def app():
1515
except (ConfigFileNotSpecified, BadConfig) as e:
1616
print(e)
1717
sys.exit(1)
18+
1819
return create_app(server_config)
1920

2021

lib/pbench/server/api/__init__.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,61 @@
1717
from pbench.common.logger import get_pbench_logger
1818
from pbench.server.api.resources.query_apis.elasticsearch_api import Elasticsearch
1919
from pbench.server.api.resources.query_apis.query_controllers import QueryControllers
20+
from pbench.server.api.resources.database import Database
2021
from pbench.server.api.resources.query_apis.query_month_indices import QueryMonthIndices
2122

23+
from pbench.server.api.resources.pbench_users import (
24+
RegisterUser,
25+
LoginAPI,
26+
UserAPI,
27+
)
28+
2229

2330
def register_endpoints(api, app, config):
2431
"""Register flask endpoints with the corresponding resource classes
2532
to make the APIs active."""
2633

2734
base_uri = config.rest_uri
28-
app.logger.info("Registering service endpoints with base URI {}", base_uri)
35+
logger = app.logger
36+
37+
logger.info("Registering service endpoints with base URI {}", base_uri)
2938

3039
api.add_resource(
3140
Upload,
3241
f"{base_uri}/upload/ctrl/<string:controller>",
33-
resource_class_args=(config, app.logger),
42+
resource_class_args=(config, logger),
3443
)
3544
api.add_resource(
36-
HostInfo, f"{base_uri}/host_info", resource_class_args=(config, app.logger),
45+
HostInfo, f"{base_uri}/host_info", resource_class_args=(config, logger),
3746
)
3847
api.add_resource(
3948
Elasticsearch,
4049
f"{base_uri}/elasticsearch",
41-
resource_class_args=(config, app.logger),
50+
resource_class_args=(config, logger),
4251
)
4352
api.add_resource(
44-
GraphQL, f"{base_uri}/graphql", resource_class_args=(config, app.logger),
53+
GraphQL, f"{base_uri}/graphql", resource_class_args=(config, logger),
4554
)
4655
api.add_resource(
4756
QueryControllers,
4857
f"{base_uri}/controllers/list",
49-
resource_class_args=(config, app.logger),
58+
resource_class_args=(config, logger),
5059
)
5160
api.add_resource(
5261
QueryMonthIndices,
5362
f"{base_uri}/controllers/months",
54-
resource_class_args=(config, app.logger),
63+
resource_class_args=(config, logger),
64+
)
65+
66+
api.add_resource(
67+
RegisterUser, f"{base_uri}/user", resource_class_args=(config, logger),
68+
)
69+
api.add_resource(
70+
LoginAPI, f"{base_uri}/session", resource_class_args=(config, logger),
71+
)
72+
73+
api.add_resource(
74+
UserAPI, f"{base_uri}/user/<string:username>", resource_class_args=(logger,),
5575
)
5676

5777

@@ -74,14 +94,21 @@ def create_app(server_config):
7494
"""Create Flask app with defined resource endpoints."""
7595

7696
app = Flask("api-server")
77-
api = Api(app)
7897
CORS(app, resources={r"/api/*": {"origins": "*"}})
7998

8099
app.logger = get_pbench_logger(__name__, server_config)
81100

82101
app.config["DEBUG"] = False
83102
app.config["TESTING"] = False
84103

104+
api = Api(app)
105+
85106
register_endpoints(api, app, server_config)
86107

108+
Database.init_db(server_config=server_config, logger=app.logger)
109+
110+
@app.teardown_appcontext
111+
def shutdown_session(exception=None):
112+
Database.db_session.remove()
113+
87114
return app
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# A generic, single database configuration.
2+
3+
[alembic]
4+
# path to migration scripts
5+
script_location = alembic
6+
7+
# template used to generate migration files
8+
# file_template = %%(rev)s_%%(slug)s
9+
10+
# timezone to use when rendering the date
11+
# within the migration file as well as the filename.
12+
# string value is passed to dateutil.tz.gettz()
13+
# leave blank for localtime
14+
# timezone =
15+
16+
# max length of characters to apply to the
17+
# "slug" field
18+
# truncate_slug_length = 40
19+
20+
# set to 'true' to run the environment during
21+
# the 'revision' command, regardless of autogenerate
22+
# revision_environment = false
23+
24+
# set to 'true' to allow .pyc and .pyo files without
25+
# a source .py file to be detected as revisions in the
26+
# versions/ directory
27+
# sourceless = false
28+
29+
# version location specification; this defaults
30+
# to alembic/versions. When using multiple version
31+
# directories, initial revisions must be specified with --version-path
32+
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
33+
34+
# the output encoding used when revision files
35+
# are written from script.py.mako
36+
# output_encoding = utf-8
37+
38+
sqlalchemy.url = driver://user:pass@localhost/dbname
39+
40+
41+
[post_write_hooks]
42+
# post_write_hooks defines scripts or Python functions that are run
43+
# on newly generated revision scripts. See the documentation for further
44+
# detail and examples
45+
46+
# format using "black" - use the console_scripts runner, against the "black" entrypoint
47+
# hooks=black
48+
# black.type=console_scripts
49+
# black.entrypoint=black
50+
# black.options=-l 79
51+
52+
# Logging configuration
53+
[loggers]
54+
keys = root,sqlalchemy,alembic
55+
56+
[handlers]
57+
keys = console
58+
59+
[formatters]
60+
keys = generic
61+
62+
[logger_root]
63+
level = WARN
64+
handlers = console
65+
qualname =
66+
67+
[logger_sqlalchemy]
68+
level = WARN
69+
handlers =
70+
qualname = sqlalchemy.engine
71+
72+
[logger_alembic]
73+
level = INFO
74+
handlers =
75+
qualname = alembic
76+
77+
[handler_console]
78+
class = StreamHandler
79+
args = (sys.stderr,)
80+
level = NOTSET
81+
formatter = generic
82+
83+
[formatter_generic]
84+
format = %(levelname)-5.5s [%(name)s] %(message)s
85+
datefmt = %H:%M:%S
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Generic single-database configuration.
2+
3+
Some useful commands to run migrations:
4+
5+
Migration commit files are stored in alembic/versions folder.
6+
7+
To create a db migration file
8+
# alembic revision — autogenerate -m “First commit”
9+
10+
Using the above command alembic generate our first migration commit file in versions folder.
11+
file names are usually stored as revision_id_<commit_message>.py
12+
13+
Once this file generates we are ready for database migration.
14+
# alembic upgrade head
15+
16+
To upgrade to specific migration
17+
# alembic upgrade <revision_id_>
18+
19+
To downgrade to specific migration
20+
# alembic downgrade <revision_id_>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""
2+
This file is auto generated when we run `alembic init alembic` but modified according to our needs.
3+
This Python script runs whenever the alembic migration tool is invoked.
4+
It contains instructions to configure and generate a SQLAlchemy engine,
5+
procure a connection from that engine along with a transaction, and then
6+
invoke the migration engine, using the connection as a source of database connectivity.
7+
"""
8+
import sys
9+
from logging.config import fileConfig
10+
11+
12+
from alembic import context
13+
14+
from pbench.common.exceptions import BadConfig, ConfigFileNotSpecified
15+
from pbench.server.api.resources.database import Database
16+
from pbench.common.logger import get_pbench_logger
17+
from pbench.server.api import get_server_config
18+
19+
# this is the Alembic Config object, which provides
20+
# access to the values within the .ini file in use.
21+
config = context.config
22+
23+
# Interpret the config file for Python logging.
24+
# This line sets up loggers basically.
25+
fileConfig(config.config_file_name)
26+
27+
# add your model's MetaData object here
28+
# for 'autogenerate' support
29+
# from myapp import mymodel
30+
# target_metadata = mymodel.Base.metadata
31+
target_metadata = [Database.Base.metadata]
32+
33+
# other values from the config, defined by the needs of env.py,
34+
# can be acquired:
35+
# my_important_option = config.get_main_option("my_important_option")
36+
# ... etc.
37+
38+
39+
def run_migrations_offline():
40+
"""Run migrations in 'offline' mode.
41+
42+
This configures the context with just a URL
43+
and not an Engine, though an Engine is acceptable
44+
here as well. By skipping the Engine creation
45+
we don't even need a DBAPI to be available.
46+
47+
Calls to context.execute() here emit the given string to the
48+
script output.
49+
50+
"""
51+
# url = config.get_main_option("sqlalchemy.url")
52+
try:
53+
server_config = get_server_config()
54+
logger = get_pbench_logger(__name__, server_config)
55+
except (ConfigFileNotSpecified, BadConfig) as e:
56+
print(e)
57+
sys.exit(1)
58+
url = Database.get_engine_uri(server_config, logger)
59+
60+
context.configure(
61+
url=url,
62+
target_metadata=target_metadata,
63+
literal_binds=True,
64+
dialect_opts={"paramstyle": "named"},
65+
)
66+
67+
with context.begin_transaction():
68+
context.run_migrations()
69+
70+
71+
def run_migrations_online():
72+
"""Run migrations in 'online' mode.
73+
74+
In this scenario we need to create an Engine
75+
and associate a connection with the context.
76+
77+
"""
78+
try:
79+
server_config = get_server_config()
80+
logger = get_pbench_logger(__name__, server_config)
81+
except (ConfigFileNotSpecified, BadConfig) as e:
82+
print(e)
83+
sys.exit(1)
84+
85+
connectable = Database.init_engine(server_config, logger)
86+
# connectable = engine_from_config(
87+
# config.get_section(config.config_ini_section),
88+
# prefix="sqlalchemy.",
89+
# poolclass=pool.NullPool,
90+
# )
91+
92+
with connectable.connect() as connection:
93+
context.configure(connection=connection, target_metadata=target_metadata)
94+
95+
with context.begin_transaction():
96+
context.run_migrations()
97+
98+
99+
if context.is_offline_mode():
100+
print("running migration offline")
101+
run_migrations_offline()
102+
else:
103+
run_migrations_online()
104+
print("running migration online")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""${message}
2+
3+
Revision ID: ${up_revision}
4+
Revises: ${down_revision | comma,n}
5+
Create Date: ${create_date}
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
${imports if imports else ""}
11+
12+
# revision identifiers, used by Alembic.
13+
revision = ${repr(up_revision)}
14+
down_revision = ${repr(down_revision)}
15+
branch_labels = ${repr(branch_labels)}
16+
depends_on = ${repr(depends_on)}
17+
18+
19+
def upgrade():
20+
${upgrades if upgrades else "pass"}
21+
22+
23+
def downgrade():
24+
${downgrades if downgrades else "pass"}

0 commit comments

Comments
 (0)