Skip to content

Commit

Permalink
✨ Add initial grafana dashboard application
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonBirchall committed Dec 10, 2024
1 parent 955de48 commit 4aad5b5
Show file tree
Hide file tree
Showing 31 changed files with 2,651 additions and 0 deletions.
70 changes: 70 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
Dockerfile
.git/
.github/

*.py[cod]

# C extensions
*.so

# Packages
*.egg
*.egg-info
dist
build
eggs
parts
var
sdist
develop-eggs
.installed.cfg
lib
lib64

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox
nosetests.xml

*.log

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

# Complexity
output/*.html
output/*/index.html

# Sphinx
docs/_build

.webassets-cache

# Virtualenvs
env
env*
venv

# intellij
.idea/
*.ipr
*.iml
*.iws

.DS_Store

# node
node_modules/

.sass-cache/

docs/
README.md
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM python:3.12.0-slim

RUN groupadd -r user && useradd -r -g user 1051

WORKDIR /home/operations-engineering-kubera

COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock
COPY app app
COPY example-data data
COPY data/production production

RUN pip3 install --no-cache-dir pipenv
RUN pipenv install --system --deploy --ignore-pipfile

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

EXPOSE 4567

USER 1051

ENTRYPOINT ["gunicorn", "--bind=0.0.0.0:4567", "app.run:app()"]
18 changes: 18 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
authlib = "==1.3.1"
dash_auth = "==2.3.0"
flask = "==3.0.0"
gunicorn = "==21.2.0"
psycopg2-binary = "2.9.9"
plotly = "==5.21.0"
dash = "==2.16.1"
pandas = "==2.2.2"
statsmodels = "==0.14.1"

[requires]
python_version = "3.12"
769 changes: 769 additions & 0 deletions Pipfile.lock

Large diffs are not rendered by default.

Empty file added app/__init__.py
Empty file.
168 changes: 168 additions & 0 deletions app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import logging

from dash import Dash, dcc, html
from dash_auth import OIDCAuth, add_public_routes
from flask import Flask

from app.config.app_config import app_config
from app.config.logging_config import configure_logging
from app.config.routes_config import configure_routes
from app.services.database_service import DatabaseService
from app.services.figure_service import FigureService

logger = logging.getLogger(__name__)


def create_app() -> Flask:
configure_logging(app_config.logging_level)

logger.info("Starting app...")

server = Flask(__name__)

server.database_service = DatabaseService()
server.figure_service = FigureService(server.database_service)

configure_routes(server)

logger.info("Populating stub data...")
server.database_service.create_indicators_table()
server.database_service.clean_stubbed_indicators_table()

app = Dash(__name__, server=server, url_base_pathname="/dashboard/")
app.title = "⚙️ SLO dashboard"
app.layout = create_dashboard(server.figure_service)

if app_config.auth_enabled:
auth = OIDCAuth(
app,
secret_key=app_config.flask.app_secret_key,
force_https_callback=True,
secure_session=True,
)
add_public_routes(app, routes=["/api/indicator/add"])
auth.register_provider(
"idp",
token_endpoint_auth_method="client_secret_post",
client_id=app_config.auth0.client_id,
client_secret=app_config.auth0.client_secret,
server_metadata_url=f"https://{app_config.auth0.domain}/.well-known/openid-configuration",
)
logger.info("Running app...")

return app.server


def create_dashboard(figure_service: FigureService):
def dashboard():
return html.Div(
children=[
html.H1("🤩 Live Data 🤩"),
dcc.Graph(
figure=figure_service.get_number_of_repositories_with_standards_label_dashboard(),
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_support_stats_year_to_date(),
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_support_stats_current_month(),
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
html.H2("Sentry Quota"),
dcc.Graph(
figure=figure_service.get_sentry_transactions_usage(),
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_sentry_errors_usage(),
style={
"width": "50%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_sentry_replays_usage(),
style={
"width": "50%",
"height": "500px",
"display": "inline-block",
},
),
html.H2("Github Actions Quota"),
dcc.Graph(
figure=figure_service.get_github_actions_quota_usage_cumulative()[
0
],
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_github_actions_quota_usage_cumulative()[
1
],
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
html.H1("🙈 Stub Data 🙈"),
dcc.Graph(
figure=figure_service.get_stubbed_number_of_repositories_with_standards_label_dashboard(),
style={
"width": "33%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_stubbed_number_of_repositories_archived_by_automation(),
style={
"width": "33%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_stubbed_sentry_transactions_used(),
style={
"width": "33%",
"height": "500px",
"display": "inline-block",
},
),
dcc.Graph(
figure=figure_service.get_support_stats(),
style={
"width": "100%",
"height": "500px",
"display": "inline-block",
},
),
],
style={"padding": "0px", "margin": "0px", "background-color": "black"},
)

return dashboard
11 changes: 11 additions & 0 deletions app/assets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

body {
margin: 0px;
font-family: monospace;
color: white;
}

h1 {
padding-top: 20px;
margin-top: 0px;
}
Empty file added app/config/__init__.py
Empty file.
43 changes: 43 additions & 0 deletions app/config/app_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os
from types import SimpleNamespace


def __get_env_var(name: str) -> str | None:
return os.getenv(name)


def __get_env_var_as_boolean(name: str, default: bool) -> bool | None:
value = __get_env_var(name)

if value is None:
return default

if value.lower() == "true":
return True

if value.lower() == "false":
return False

return default


app_config = SimpleNamespace(
api_key=__get_env_var("API_KEY"),
auth_enabled=__get_env_var_as_boolean("AUTH_ENABLED", True),
auth0=SimpleNamespace(
domain=__get_env_var("AUTH0_DOMAIN"),
client_id=__get_env_var("AUTH0_CLIENT_ID"),
client_secret=__get_env_var("AUTH0_CLIENT_SECRET"),
),
flask=SimpleNamespace(
app_secret_key=__get_env_var("APP_SECRET_KEY"),
),
logging_level=__get_env_var("LOGGING_LEVEL"),
postgres=SimpleNamespace(
user=__get_env_var("POSTGRES_USER"),
password=__get_env_var("POSTGRES_PASSWORD"),
db=__get_env_var("POSTGRES_DB"),
host=__get_env_var("POSTGRES_HOST"),
port=__get_env_var("POSTGRES_PORT"),
),
)
10 changes: 10 additions & 0 deletions app/config/logging_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import logging


def configure_logging(logging_level: str) -> None:
logging.basicConfig(
format="{asctime:s} | {levelname:>8s} | {filename:s}:{lineno:d} | {message:s}",
datefmt="%Y-%m-%dT%H:%M:%S",
style="{",
level=logging_level.upper() if logging_level else "INFO",
)
9 changes: 9 additions & 0 deletions app/config/routes_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from flask import Flask

from app.routes.api_route import api_route
from app.routes.index_route import index_route


def configure_routes(app: Flask) -> None:
app.register_blueprint(api_route, url_prefix="/api")
app.register_blueprint(index_route, url_prefix="/")
28 changes: 28 additions & 0 deletions app/routes/api_route.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import logging
from typing import Callable

from flask import Blueprint, current_app, request

from app.config.app_config import app_config

logger = logging.getLogger(__name__)

api_route = Blueprint("api_routes", __name__)


def requires_api_key(func: Callable):
def decorator(*args, **kwargs):
if "X-API-KEY" not in request.headers or request.headers.get("X-API-KEY") != app_config.api_key:
return "", 403
return func(*args, **kwargs)

return decorator


@api_route.route("/indicator/add", methods=["POST"])
@requires_api_key
def add_indicator():
indicator = request.get_json().get("indicator")
count = request.get_json().get("count")
current_app.database_service.add_indicator(indicator, count)
return ("", 204)
Loading

0 comments on commit 4aad5b5

Please sign in to comment.