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:
parent
1db8a22ca9
commit
f55c0d77dc
|
|
@ -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"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
from flask import Blueprint
|
||||
|
||||
bp = Blueprint("base_routes", __name__)
|
||||
|
||||
from . import get, post, delete # noqa E402, F401
|
||||
|
|
@ -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.")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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.")
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
Loading…
Reference in New Issue