--node-link-url-template and --pod-link-url-template for external links
This commit is contained in:
4
Makefile
4
Makefile
@@ -27,4 +27,6 @@ push: docker
|
|||||||
docker push "$(IMAGE):$(TAG)"
|
docker push "$(IMAGE):$(TAG)"
|
||||||
|
|
||||||
mock:
|
mock:
|
||||||
docker run $(TTYFLAGS) -p 8080:8080 "$(IMAGE):$(TAG)" --mock
|
docker run $(TTYFLAGS) -p 8080:8080 "$(IMAGE):$(TAG)" --mock \
|
||||||
|
--node-link-url-template "https://kube-web-view.example.org/clusters/{cluster}/nodes/{name}" \
|
||||||
|
--pod-link-url-template "https://kube-web-view.example.org/clusters/{cluster}/namespaces/{namespace}/pods/{name}"
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ Kubernetes Operational View
|
|||||||
:target: https://hub.docker.com/r/hjacobs/kube-ops-view
|
:target: https://hub.docker.com/r/hjacobs/kube-ops-view
|
||||||
:alt: Docker pulls
|
:alt: Docker pulls
|
||||||
|
|
||||||
**This project is in a very early state, but it might already be useful.**
|
|
||||||
|
|
||||||
.. image:: screenshot.png
|
.. image:: screenshot.png
|
||||||
:alt: Screenshot
|
:alt: Screenshot
|
||||||
|
|
||||||
@@ -157,6 +155,10 @@ The following environment variables are supported:
|
|||||||
Optional Redis server to use for pub/sub events and job locking when running more than one replica. Example: ``redis://my-redis:6379``
|
Optional Redis server to use for pub/sub events and job locking when running more than one replica. Example: ``redis://my-redis:6379``
|
||||||
``SERVER_PORT``
|
``SERVER_PORT``
|
||||||
HTTP port to listen on. It defaults to ``8080``.
|
HTTP port to listen on. It defaults to ``8080``.
|
||||||
|
``NODE_LINK_URL_TEMPLATE``
|
||||||
|
Template to make Nodes clickable, e.g. can point to `kube-web-view <https://codeberg.org/hjacobs/kube-web-view/>`_. ``{cluster}`` (cluster ID) and ``{name}`` (Node name) will be replaced in the URL template.
|
||||||
|
``POD_LINK_URL_TEMPLATE``
|
||||||
|
Template to make Pods clickable, e.g. can point to `kube-web-view <https://codeberg.org/hjacobs/kube-web-view/>`_. ``{cluster}`` (cluster ID), ``{namespace}`` (Pod's namespace), and ``{name}`` (Pod name) will be replaced in the URL template.
|
||||||
|
|
||||||
|
|
||||||
Supported Browsers
|
Supported Browsers
|
||||||
|
|||||||
@@ -14,9 +14,13 @@ const addWheelListener = require('./vendor/addWheelListener')
|
|||||||
|
|
||||||
export default class App {
|
export default class App {
|
||||||
|
|
||||||
constructor() {
|
constructor(config) {
|
||||||
const params = this.parseLocationHash()
|
const params = this.parseLocationHash()
|
||||||
|
|
||||||
this.config = Config.fromParams(params)
|
this.config = Config.fromParams(params)
|
||||||
|
this.config.nodeLinkUrlTemplate = config['node_link_url_template']
|
||||||
|
this.config.podLinkUrlTemplate = config['pod_link_url_template']
|
||||||
|
|
||||||
this.filterString = (params.get('q') && decodeURIComponent(params.get('q'))) || ''
|
this.filterString = (params.get('q') && decodeURIComponent(params.get('q'))) || ''
|
||||||
this.selectedClusters = new Set((params.get('clusters') || '').split(',').filter(x => x))
|
this.selectedClusters = new Set((params.get('clusters') || '').split(',').filter(x => x))
|
||||||
this.seenPods = new Set()
|
this.seenPods = new Set()
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ export default class Config {
|
|||||||
this.maxConnectionLifetimeSeconds = 300
|
this.maxConnectionLifetimeSeconds = 300
|
||||||
// consider cluster data older than 1 minute outdated
|
// consider cluster data older than 1 minute outdated
|
||||||
this.maxDataAgeSeconds = 60
|
this.maxDataAgeSeconds = 60
|
||||||
|
|
||||||
|
this.nodeLinkUrlTemplate = null
|
||||||
|
this.podLinkUrlTemplate = null
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromParams(params) {
|
static fromParams(params) {
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export class Node extends PIXI.Graphics {
|
|||||||
topHandle.beginFill(App.current.theme.primaryColor, 1)
|
topHandle.beginFill(App.current.theme.primaryColor, 1)
|
||||||
topHandle.drawRect(0, 0, this.widthOfNodePx, App.current.heightOfTopHandlePx)
|
topHandle.drawRect(0, 0, this.widthOfNodePx, App.current.heightOfTopHandlePx)
|
||||||
topHandle.endFill()
|
topHandle.endFill()
|
||||||
|
|
||||||
// there is about 2.83 letters per pod
|
// there is about 2.83 letters per pod
|
||||||
const roomForText = Math.floor(2.83 * this.podsPerRow)
|
const roomForText = Math.floor(2.83 * this.podsPerRow)
|
||||||
const ellipsizedNodeName = this.node.name.length > roomForText ? this.node.name.substring(0, roomForText).concat('…') : this.node.name
|
const ellipsizedNodeName = this.node.name.length > roomForText ? this.node.name.substring(0, roomForText).concat('…') : this.node.name
|
||||||
@@ -97,6 +98,12 @@ export class Node extends PIXI.Graphics {
|
|||||||
topHandle.on('mouseout', function () {
|
topHandle.on('mouseout', function () {
|
||||||
nodeBox.tooltip.visible = false
|
nodeBox.tooltip.visible = false
|
||||||
})
|
})
|
||||||
|
if (App.current.config.nodeLinkUrlTemplate !== null) {
|
||||||
|
topHandle.buttonMode = true
|
||||||
|
topHandle.on('click', function() {
|
||||||
|
location.href = App.current.config.nodeLinkUrlTemplate.replace('{cluster}', nodeBox.cluster.cluster.id).replace('{name}', nodeBox.node.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
const resources = this.getResourceUsage()
|
const resources = this.getResourceUsage()
|
||||||
const bars = new Bars(nodeBox, resources, nodeBox.tooltip)
|
const bars = new Bars(nodeBox, resources, nodeBox.tooltip)
|
||||||
bars.x = 0
|
bars.x = 0
|
||||||
|
|||||||
@@ -228,6 +228,12 @@ export class Pod extends PIXI.Graphics {
|
|||||||
podBox.filters = podBox.filters.filter(x => x != BRIGHTNESS_FILTER)
|
podBox.filters = podBox.filters.filter(x => x != BRIGHTNESS_FILTER)
|
||||||
this.tooltip.visible = false
|
this.tooltip.visible = false
|
||||||
})
|
})
|
||||||
|
if (App.current.config.podLinkUrlTemplate !== null) {
|
||||||
|
podBox.buttonMode = true
|
||||||
|
podBox.on('click', function() {
|
||||||
|
location.href = App.current.config.podLinkUrlTemplate.replace('{cluster}', this.cluster.cluster.id).replace('{namespace}', this.pod.namespace).replace('{name}', this.pod.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
podBox.lineStyle(1, App.current.theme.primaryColor, 1)
|
podBox.lineStyle(1, App.current.theme.primaryColor, 1)
|
||||||
const w = 10 / this.pod.containers.length
|
const w = 10 / this.pod.containers.length
|
||||||
for (let i = 0; i < this.pod.containers.length; i++) {
|
for (let i = 0; i < this.pod.containers.length; i++) {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ def index():
|
|||||||
else:
|
else:
|
||||||
logger.error('Could not find JavaScript application bundle app*.js in {}'.format(static_build_path))
|
logger.error('Could not find JavaScript application bundle app*.js in {}'.format(static_build_path))
|
||||||
flask.abort(503, 'JavaScript application bundle not found (missing build)')
|
flask.abort(503, 'JavaScript application bundle not found (missing build)')
|
||||||
return flask.render_template('index.html', app_js=app_js, version=kube_ops_view.__version__)
|
return flask.render_template('index.html', app_js=app_js, version=kube_ops_view.__version__, app_config_json=json.dumps(app.app_config))
|
||||||
|
|
||||||
|
|
||||||
def event(cluster_ids: set):
|
def event(cluster_ids: set):
|
||||||
@@ -188,7 +188,10 @@ class CommaSeparatedValues(click.ParamType):
|
|||||||
@click.option('--kubeconfig-contexts', type=CommaSeparatedValues(),
|
@click.option('--kubeconfig-contexts', type=CommaSeparatedValues(),
|
||||||
help='List of kubeconfig contexts to use (default: use all defined contexts)', envvar='KUBECONFIG_CONTEXTS')
|
help='List of kubeconfig contexts to use (default: use all defined contexts)', envvar='KUBECONFIG_CONTEXTS')
|
||||||
@click.option('--query-interval', type=float, help='Interval in seconds for querying clusters (default: 5)', envvar='QUERY_INTERVAL', default=5)
|
@click.option('--query-interval', type=float, help='Interval in seconds for querying clusters (default: 5)', envvar='QUERY_INTERVAL', default=5)
|
||||||
def main(port, debug, mock, secret_key, redis_url, clusters: list, cluster_registry_url, kubeconfig_path, kubeconfig_contexts: list, query_interval):
|
@click.option('--node-link-url-template', help='Template for target URL when clicking on a Node', envvar='NODE_LINK_URL_TEMPLATE')
|
||||||
|
@click.option('--pod-link-url-template', help='Template for target URL when clicking on a Pod', envvar='POD_LINK_URL_TEMPLATE')
|
||||||
|
def main(port, debug, mock, secret_key, redis_url, clusters: list, cluster_registry_url, kubeconfig_path, kubeconfig_contexts: list, query_interval,
|
||||||
|
node_link_url_template: str, pod_link_url_template: str):
|
||||||
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO)
|
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO)
|
||||||
|
|
||||||
store = RedisStore(redis_url) if redis_url else MemoryStore()
|
store = RedisStore(redis_url) if redis_url else MemoryStore()
|
||||||
@@ -196,6 +199,7 @@ def main(port, debug, mock, secret_key, redis_url, clusters: list, cluster_regis
|
|||||||
app.debug = debug
|
app.debug = debug
|
||||||
app.secret_key = secret_key
|
app.secret_key = secret_key
|
||||||
app.store = store
|
app.store = store
|
||||||
|
app.app_config = {'node_link_url_template': node_link_url_template, 'pod_link_url_template': pod_link_url_template}
|
||||||
|
|
||||||
if mock:
|
if mock:
|
||||||
cluster_query = query_mock_cluster
|
cluster_query = query_mock_cluster
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
<!-- make sure the font is loaded -->
|
<!-- make sure the font is loaded -->
|
||||||
<div id="loading" style="font-family: ShareTechMono">Loading..</div>
|
<div id="loading" style="font-family: ShareTechMono">Loading..</div>
|
||||||
<script src="static/build/{{ app_js }}"></script>
|
<script src="static/build/{{ app_js }}"></script>
|
||||||
<script>document.getElementById('loading').style.display = 'none'; const app = new App(); app.run()</script>
|
<script>document.getElementById('loading').style.display = 'none'; const app = new App({{ app_config_json|safe }}); app.run()</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user