mirror of
https://github.com/nikdoof/ohayodash.git
synced 2025-12-13 01:52:18 +00:00
Modernise Python support
This commit is contained in:
4
Makefile
4
Makefile
@@ -1,4 +1,6 @@
|
|||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
uv run --dev ruff check --output-format=github --select=E9,F63,F7,F82 --target-version=py39 .
|
uv run --dev ruff check --output-format=github --select=E9,F63,F7,F82 --target-version=py39 .
|
||||||
uv run --dev -m ruff check --output-format=github --target-version=py39 .
|
uv run --dev -m ruff check --output-format=github --target-version=py39 .
|
||||||
|
serve:
|
||||||
|
uv run --dev -m flask --app ohayodash.app:app run
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
|
import importlib.resources
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import kubernetes
|
import kubernetes
|
||||||
import pkg_resources
|
|
||||||
import yaml
|
import yaml
|
||||||
from flask import Blueprint, jsonify, render_template
|
from flask import Blueprint, jsonify, render_template
|
||||||
|
|
||||||
ANNOTATION_BASE = 'ohayodash.github.io'
|
ANNOTATION_BASE = "ohayodash.github.io"
|
||||||
|
|
||||||
base = Blueprint('base', __name__, template_folder='templates')
|
base = Blueprint("base", __name__, template_folder="templates")
|
||||||
|
|
||||||
if 'KUBERNETES_SERVICE_HOST' in os.environ:
|
if "KUBERNETES_SERVICE_HOST" in os.environ:
|
||||||
kubernetes.config.load_incluster_config()
|
kubernetes.config.load_incluster_config()
|
||||||
else:
|
else:
|
||||||
kubernetes.config.load_kube_config()
|
kubernetes.config.load_kube_config()
|
||||||
@@ -17,8 +17,8 @@ else:
|
|||||||
|
|
||||||
def check_tags(tag, kubeobj):
|
def check_tags(tag, kubeobj):
|
||||||
# Skip if we're limited to a tag, and the CM has tags but not that one.
|
# Skip if we're limited to a tag, and the CM has tags but not that one.
|
||||||
tags = kubeobj.metadata.annotations.get('{0}/tags'.format(ANNOTATION_BASE), '')
|
tags = kubeobj.metadata.annotations.get("{0}/tags".format(ANNOTATION_BASE), "")
|
||||||
obj_tags = {tagname for tagname in tags.split(',') if tagname != ''}
|
obj_tags = {tagname for tagname in tags.split(",") if tagname != ""}
|
||||||
|
|
||||||
# If its not tagged, allow
|
# If its not tagged, allow
|
||||||
if not obj_tags:
|
if not obj_tags:
|
||||||
@@ -28,18 +28,17 @@ def check_tags(tag, kubeobj):
|
|||||||
return tag in obj_tags
|
return tag in obj_tags
|
||||||
|
|
||||||
|
|
||||||
def get_k8s_applications(tag: str = None) -> list:
|
def get_k8s_applications(tag: str | None = None) -> list:
|
||||||
"""Get all ingresses from the cluster and produce a application list."""
|
"""Get all ingresses from the cluster and produce a application list."""
|
||||||
api = kubernetes.client.NetworkingV1Api()
|
api = kubernetes.client.NetworkingV1Api()
|
||||||
|
|
||||||
applications = []
|
applications = []
|
||||||
for ingress in api.list_ingress_for_all_namespaces(watch=False).items:
|
for ingress in api.list_ingress_for_all_namespaces(watch=False).items:
|
||||||
|
|
||||||
# Skip if not enabled
|
# Skip if not enabled
|
||||||
enable_annotation = '{0}/enable'.format(ANNOTATION_BASE)
|
enable_annotation = "{0}/enable".format(ANNOTATION_BASE)
|
||||||
if enable_annotation not in ingress.metadata.annotations:
|
if enable_annotation not in ingress.metadata.annotations:
|
||||||
continue
|
continue
|
||||||
if ingress.metadata.annotations[enable_annotation] == 'false':
|
if ingress.metadata.annotations[enable_annotation] == "false":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip if we're limited to a tag, and the Ingress has tags but not that one.
|
# Skip if we're limited to a tag, and the Ingress has tags but not that one.
|
||||||
@@ -48,23 +47,23 @@ def get_k8s_applications(tag: str = None) -> list:
|
|||||||
|
|
||||||
# Set to some basic values from the ingress
|
# Set to some basic values from the ingress
|
||||||
application_values = {
|
application_values = {
|
||||||
'name': ingress.metadata.name,
|
"name": ingress.metadata.name,
|
||||||
'namespace': ingress.metadata.namespace,
|
"namespace": ingress.metadata.namespace,
|
||||||
'url': 'https://{0}'.format(ingress.spec.rules[0].host),
|
"url": "https://{0}".format(ingress.spec.rules[0].host),
|
||||||
'show_url': False,
|
"show_url": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Read annotations and override the values if defined
|
# Read annotations and override the values if defined
|
||||||
for key, value in ingress.metadata.annotations.items():
|
for key, value in ingress.metadata.annotations.items():
|
||||||
if key.startswith(ANNOTATION_BASE):
|
if key.startswith(ANNOTATION_BASE):
|
||||||
annotation_key = key.split('/')[1]
|
annotation_key = key.split("/")[1]
|
||||||
application_values[annotation_key] = value
|
application_values[annotation_key] = value
|
||||||
|
|
||||||
applications.append(application_values)
|
applications.append(application_values)
|
||||||
return sorted(applications, key=lambda item: item['name'])
|
return sorted(applications, key=lambda item: item["name"])
|
||||||
|
|
||||||
|
|
||||||
def get_bookmarks(tag: str = None) -> list:
|
def get_bookmarks(tag: str | None = None) -> list:
|
||||||
"""Get all 'bookmark' ConfigMaps from the cluster and produce a bookmark list."""
|
"""Get all 'bookmark' ConfigMaps from the cluster and produce a bookmark list."""
|
||||||
v1 = kubernetes.client.CoreV1Api()
|
v1 = kubernetes.client.CoreV1Api()
|
||||||
ret = v1.list_config_map_for_all_namespaces(watch=False)
|
ret = v1.list_config_map_for_all_namespaces(watch=False)
|
||||||
@@ -76,7 +75,7 @@ def get_bookmarks(tag: str = None) -> list:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip if its not tagged as bookmark CM
|
# Skip if its not tagged as bookmark CM
|
||||||
if '{0}/bookmarks'.format(ANNOTATION_BASE) not in cm.metadata.annotations:
|
if "{0}/bookmarks".format(ANNOTATION_BASE) not in cm.metadata.annotations:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip if we're limited to a tag, and the CM has tags but not that one.
|
# Skip if we're limited to a tag, and the CM has tags but not that one.
|
||||||
@@ -84,27 +83,27 @@ def get_bookmarks(tag: str = None) -> list:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Load bookmark data
|
# Load bookmark data
|
||||||
bookmark_data = yaml.safe_load(cm.data['bookmarks'])
|
bookmark_data = yaml.safe_load(cm.data["bookmarks"])
|
||||||
|
|
||||||
# Iterate each bookmark
|
# Iterate each bookmark
|
||||||
for bookmark in bookmark_data:
|
for bookmark in bookmark_data:
|
||||||
if 'group' not in bookmark:
|
if "group" not in bookmark:
|
||||||
group = 'default'
|
group = "default"
|
||||||
else:
|
else:
|
||||||
group = bookmark['group'].lower()
|
group = bookmark["group"].lower()
|
||||||
|
|
||||||
# Find category dict and append or create
|
# Find category dict and append or create
|
||||||
for cat in bookmarks:
|
for cat in bookmarks:
|
||||||
if cat['category'] == group:
|
if cat["category"] == group:
|
||||||
cat['links'].append(bookmark)
|
cat["links"].append(bookmark)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
bookmarks.append({'category': group, 'links': [bookmark]})
|
bookmarks.append({"category": group, "links": [bookmark]})
|
||||||
|
|
||||||
return bookmarks
|
return bookmarks
|
||||||
|
|
||||||
|
|
||||||
def get_providers(tag: str = None) -> list:
|
def get_providers(tag: str | None = None) -> list:
|
||||||
"""Get all 'provider' ConfigMaps from the cluster and produce a provider list."""
|
"""Get all 'provider' ConfigMaps from the cluster and produce a provider list."""
|
||||||
v1 = kubernetes.client.CoreV1Api()
|
v1 = kubernetes.client.CoreV1Api()
|
||||||
ret = v1.list_config_map_for_all_namespaces(watch=False)
|
ret = v1.list_config_map_for_all_namespaces(watch=False)
|
||||||
@@ -116,47 +115,52 @@ def get_providers(tag: str = None) -> list:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip if its not tagged as bookmark CM
|
# Skip if its not tagged as bookmark CM
|
||||||
if '{0}/providers'.format(ANNOTATION_BASE) not in cm.metadata.annotations:
|
if "{0}/providers".format(ANNOTATION_BASE) not in cm.metadata.annotations:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip if we're limited to a tag, and the CM has tags but not that one.
|
# Skip if we're limited to a tag, and the CM has tags but not that one.
|
||||||
if not check_tags(tag, cm):
|
if not check_tags(tag, cm):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
provider_data = yaml.safe_load(cm.data['providers'])
|
provider_data = yaml.safe_load(cm.data["providers"])
|
||||||
providers.extend(provider_data)
|
providers.extend(provider_data)
|
||||||
|
|
||||||
return providers
|
return providers
|
||||||
|
|
||||||
|
|
||||||
@base.route('/')
|
@base.route("/")
|
||||||
@base.route('/<tag>/')
|
@base.route("/<tag>/")
|
||||||
def index(tag=None):
|
def index(tag=None):
|
||||||
return render_template('index.j2')
|
return render_template("index.j2")
|
||||||
|
|
||||||
|
|
||||||
@base.route('/providers.json')
|
@base.route("/providers.json")
|
||||||
@base.route('/<tag>/providers.json')
|
@base.route("/<tag>/providers.json")
|
||||||
def providers(tag=None):
|
def providers(tag=None):
|
||||||
k8s_providers = get_providers(tag)
|
k8s_providers = get_providers(tag)
|
||||||
if not k8s_providers:
|
if not k8s_providers:
|
||||||
data_file = pkg_resources.resource_filename(__name__, 'data/providers.yaml')
|
data_file = importlib.resources.files(__name__) / "data/providers.yaml"
|
||||||
with open(data_file, 'r') as fobj:
|
with importlib.resources.as_file(data_file) as path:
|
||||||
k8s_providers.extend(yaml.safe_load(fobj))
|
with path.open("r") as fobj:
|
||||||
return jsonify({'providers': k8s_providers})
|
k8s_providers.extend(yaml.safe_load(fobj))
|
||||||
|
return jsonify({"providers": k8s_providers})
|
||||||
|
|
||||||
|
|
||||||
@base.route('/apps.json')
|
@base.route("/apps.json")
|
||||||
@base.route('/<tag>/apps.json')
|
@base.route("/<tag>/apps.json")
|
||||||
def applications(tag=None):
|
def applications(tag=None):
|
||||||
return jsonify({
|
return jsonify(
|
||||||
'apps': get_k8s_applications(tag),
|
{
|
||||||
})
|
"apps": get_k8s_applications(tag),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@base.route('/links.json')
|
@base.route("/links.json")
|
||||||
@base.route('/<tag>/links.json')
|
@base.route("/<tag>/links.json")
|
||||||
def bookmarks(tag=None):
|
def bookmarks(tag=None):
|
||||||
return jsonify({
|
return jsonify(
|
||||||
'bookmarks': get_bookmarks(tag),
|
{
|
||||||
})
|
"bookmarks": get_bookmarks(tag),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ dependencies = [
|
|||||||
"gunicorn>=23.0.0",
|
"gunicorn>=23.0.0",
|
||||||
"kubernetes>=33.1.0",
|
"kubernetes>=33.1.0",
|
||||||
"pyyaml>=6.0.2",
|
"pyyaml>=6.0.2",
|
||||||
"setuptools>=80.9.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
|
|||||||
11
uv.lock
generated
11
uv.lock
generated
@@ -240,7 +240,6 @@ dependencies = [
|
|||||||
{ name = "gunicorn" },
|
{ name = "gunicorn" },
|
||||||
{ name = "kubernetes" },
|
{ name = "kubernetes" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml" },
|
||||||
{ name = "setuptools" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dev-dependencies]
|
[package.dev-dependencies]
|
||||||
@@ -255,7 +254,6 @@ requires-dist = [
|
|||||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||||
{ name = "kubernetes", specifier = ">=33.1.0" },
|
{ name = "kubernetes", specifier = ">=33.1.0" },
|
||||||
{ name = "pyyaml", specifier = ">=6.0.2" },
|
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||||
{ name = "setuptools", specifier = ">=80.9.0" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
@@ -423,15 +421,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" },
|
{ url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "setuptools"
|
|
||||||
version = "80.9.0"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user