tensorboard web-app: Create Tensorboard web-app backend (kubeflow/kubeflow#5180)

* Create Tensorboard web-app backend

Create the code for the Tensorboard web-app backend which
includes routes for GET, POST and DELETE requests.

The backend is created with Python/Flask, so it also uses
the common code from 'kubeflow.kubeflow.crud_backend'.

* Add 'get_age(k8s_object)' function to 'crud_backend' common code

It would be useful for all web apps of the 'crud-web-apps' folder
to return age information to their frontends.

As a result, 'get_age(k8s_object)' was added to the common code,
so that all web apps can use it.
This commit is contained in:
Konstantinos Andriopoulos 2020-08-20 13:25:22 +03:00 committed by GitHub
parent 1db8a22ca9
commit f55c0d77dc
9 changed files with 183 additions and 0 deletions

View File

@ -101,3 +101,18 @@ def get_uptime(then):
age = str(mins) + " mins"
return age + " ago"
def get_age(k8s_object):
"""
k8s_object: k8s custom resource | dictionary
Return a dictionary that contains the creationTimestamp (timestamp) of the
given k8s object and the amount of time that has passed from that timestamp
(uptime).
"""
return {
"uptime": get_uptime(
k8s_object["metadata"]["creationTimestamp"]),
"timestamp": k8s_object["metadata"]["creationTimestamp"],
}

View File

@ -0,0 +1,9 @@
install-deps:
cd ../../common/backend && pip install -e .
run:
gunicorn -w 3 --bind 0.0.0.0:5000 --access-logfile - entrypoint:_app
run-dev:
BACKEND_MODE=dev \
python entrypoint.py

View File

@ -0,0 +1,17 @@
import kubeflow.kubeflow.crud_backend as base
from kubeflow.kubeflow.crud_backend import config, logging
from .routes import bp as routes_bp
log = logging.getLogger(__name__)
def create_app(
name=__name__, static_folder="static", config_class=config.Config
):
app = base.create_app(name, static_folder, config_class)
# Register the app's blueprints
app.register_blueprint(routes_bp)
return app

View File

@ -0,0 +1,5 @@
from flask import Blueprint
bp = Blueprint("base_routes", __name__)
from . import get, post, delete # noqa E402, F401

View File

@ -0,0 +1,28 @@
from kubeflow.kubeflow.crud_backend import api, logging
from . import bp
log = logging.getLogger(__name__)
@bp.route(
"/api/namespaces/<namespace>/tensorboards/<tensorboard>",
methods=["DELETE"],
)
def delete_tensorboard(tensorboard, namespace):
log.info("About to delete Tensorboard %s/%s", tensorboard, namespace)
api.delete_custom_rsrc(
"tensorboard.kubeflow.org",
"v1alpha1",
"tensorboards",
tensorboard,
namespace,
)
log.info(
"DELETE request was sent to the API Server for Tensorboard: %s/%s",
tensorboard,
namespace,
)
return api.success_response("message", "Tensorboard deleted successfully.")

View File

@ -0,0 +1,20 @@
from kubeflow.kubeflow.crud_backend import api, logging
from .. import utils
from . import bp
log = logging.getLogger(__name__)
@bp.route("/api/namespaces/<namespace>/tensorboards")
def get_tensorboards(namespace):
tensorboards = api.list_custom_rsrc(
"tensorboard.kubeflow.org", "v1alpha1", "tensorboards", namespace
)
content = [
utils.parse_tensorboard(tensorboard)
for tensorboard in tensorboards["items"]
]
return api.success_response("tensorboards", content)

View File

@ -0,0 +1,38 @@
from flask import request
from kubeflow.kubeflow.crud_backend import (
api,
decorators,
logging,
)
from .. import utils
from . import bp
log = logging.getLogger(__name__)
@bp.route("/api/namespaces/<namespace>/tensorboards", methods=["POST"])
@decorators.request_is_json_type
@decorators.required_body_params("name", "logspath")
def post_tensorboard(namespace):
body = request.get_json()
log.info("Got body: ", body)
name = body["name"]
tensorboard = utils.get_tensorboard_dict(namespace, body)
log.info("About to create Tensorboard: %s", tensorboard)
api.create_custom_rsrc(
"tensorboard.kubeflow.org",
"v1alpha1",
"tensorboards",
tensorboard,
namespace,
)
log.info(
"Successfully created Tensorboard %s in namespace %s", name, namespace
)
return api.success_response("message", "Tensorboard created successfully.")

View File

@ -0,0 +1,31 @@
from kubeflow.kubeflow.crud_backend import helpers
def parse_tensorboard(tensorboard):
"""
Process the Tensorboard object and format it as the UI expects it.
"""
parsed_tensorboard = {
"name": tensorboard["metadata"]["name"],
"namespace": tensorboard["metadata"]["namespace"],
"logspath": tensorboard["spec"]["logspath"],
"age": helpers.get_age(tensorboard),
}
return parsed_tensorboard
def get_tensorboard_dict(namespace, body):
"""
Create Tensorboard object from request body and format it as a Python dict.
"""
tensorboard = {
"apiVersion": "tensorboard.kubeflow.org/v1alpha1",
"kind": "Tensorboard",
"metadata": {"name": body["name"], "namespace": namespace, },
"spec": {"logspath": body["logspath"], },
}
return tensorboard

View File

@ -0,0 +1,20 @@
import os
from kubeflow.kubeflow.crud_backend import config, logging
import app
log = logging.getLogger(__name__)
APP_NAME = os.environ.get("APP_NAME", "Tensorboard Web App")
BACKEND_MODE = os.environ.get("BACKEND_MODE", "prod") # 'prod' or 'dev'
# Load the Dev config based on BACKEND_MODE env var
if BACKEND_MODE == "dev":
cfg = config.DevConfig
else:
cfg = config.Config
_app = app.create_app(name=APP_NAME, config_class=cfg)
if __name__ == "__main__":
_app.run()