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"
|
age = str(mins) + " mins"
|
||||||
|
|
||||||
return age + " ago"
|
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