Merge branch 'master' into oauth2
This commit is contained in:
25
app.py
25
app.py
@@ -83,7 +83,12 @@ tokens.manage('read-only')
|
||||
@app.route('/')
|
||||
@authorize
|
||||
def index():
|
||||
return flask.render_template('index.html')
|
||||
app_js = None
|
||||
for entry in os.listdir('static'):
|
||||
if entry.startswith('app'):
|
||||
app_js = entry
|
||||
break
|
||||
return flask.render_template('index.html', app_js=app_js)
|
||||
|
||||
|
||||
@app.route('/kubernetes-clusters')
|
||||
@@ -98,6 +103,7 @@ def get_clusters():
|
||||
response.raise_for_status()
|
||||
nodes = []
|
||||
nodes_by_name = {}
|
||||
unassigned_pods = []
|
||||
for node in response.json()['items']:
|
||||
obj = {'name': node['metadata']['name'], 'labels': node['metadata']['labels'], 'status': node['status'], 'pods': []}
|
||||
nodes.append(obj)
|
||||
@@ -105,14 +111,15 @@ def get_clusters():
|
||||
response = session.get(urljoin(api_server_url, '/api/v1/pods'), timeout=5)
|
||||
response.raise_for_status()
|
||||
for pod in response.json()['items']:
|
||||
if 'nodeName' in pod['spec']:
|
||||
obj = {'name': pod['metadata']['name'],
|
||||
'namespace': pod['metadata']['namespace'],
|
||||
'labels': pod['metadata']['labels'], 'status': pod['status'], 'containers': []}
|
||||
for cont in pod['spec']['containers']:
|
||||
obj['containers'].append({'name': cont['name'], 'image': cont['image'], 'resources': cont['resources']})
|
||||
# TODO: filter pod attributes
|
||||
obj = {'name': pod['metadata']['name'],
|
||||
'namespace': pod['metadata']['namespace'],
|
||||
'labels': pod['metadata'].get('labels', {}), 'status': pod['status'], 'containers': []}
|
||||
for cont in pod['spec']['containers']:
|
||||
obj['containers'].append({'name': cont['name'], 'image': cont['image'], 'resources': cont['resources']})
|
||||
if 'nodeName' in pod['spec'] and pod['spec']['nodeName'] in nodes_by_name:
|
||||
nodes_by_name[pod['spec']['nodeName']]['pods'].append(obj)
|
||||
else:
|
||||
unassigned_pods.append(obj)
|
||||
|
||||
try:
|
||||
response = session.get(urljoin(api_server_url, '/api/v1/namespaces/kube-system/services/heapster/proxy/apis/metrics/v1alpha1/nodes'), timeout=5)
|
||||
@@ -121,7 +128,7 @@ def get_clusters():
|
||||
nodes_by_name[metrics['metadata']['name']]['usage'] = metrics['usage']
|
||||
except:
|
||||
logging.exception('Failed to get metrics')
|
||||
clusters.append({'api_server_url': api_server_url, 'nodes': nodes})
|
||||
clusters.append({'api_server_url': api_server_url, 'nodes': nodes, 'unassigned_pods': unassigned_pods})
|
||||
|
||||
return json.dumps({'kubernetes_clusters': clusters}, separators=(',', ':'))
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Node from './node.js'
|
||||
import Pod from './pod.js'
|
||||
const PIXI = require('pixi.js')
|
||||
|
||||
export default class Cluster extends PIXI.Graphics {
|
||||
@@ -24,6 +25,17 @@ export default class Cluster extends PIXI.Graphics {
|
||||
}
|
||||
this.addChild(nodeBox)
|
||||
}
|
||||
|
||||
|
||||
for (var pod of this.cluster.unassigned_pods) {
|
||||
var podBox = new Pod(pod, this.tooltip)
|
||||
podBox.x = rows[0]
|
||||
podBox.y = 20
|
||||
podBox.draw()
|
||||
this.addChild(podBox)
|
||||
rows[0] += 20
|
||||
}
|
||||
|
||||
this.lineStyle(2, 0xaaaaff, 1);
|
||||
const width = Math.max(rows[0], rows[1])
|
||||
this.drawRect(0, 0, width, nodeBox.height * 2 + 30);
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
import Pod from './pod.js'
|
||||
const PIXI = require('pixi.js')
|
||||
|
||||
// see https://github.com/kubernetes/kubernetes/blob/master/docs/design/resources.md
|
||||
const FACTORS = {
|
||||
'm': 1/1000,
|
||||
'K': 1000,
|
||||
'M': Math.pow(1000, 2),
|
||||
'G': Math.pow(1000, 3),
|
||||
'T': Math.pow(1000, 4),
|
||||
'P': Math.pow(1000, 5),
|
||||
'E': Math.pow(1000, 6),
|
||||
'Ki': 1024,
|
||||
'Mi': Math.pow(1024, 2),
|
||||
'Gi': Math.pow(1024, 3),
|
||||
'Ti': Math.pow(1024, 4),
|
||||
'Pi': Math.pow(1024, 5),
|
||||
'Ei': Math.pow(1024, 6)
|
||||
}
|
||||
|
||||
export default class Node extends PIXI.Graphics {
|
||||
constructor (node, tooltip) {
|
||||
super()
|
||||
@@ -13,16 +30,39 @@ export default class Node extends PIXI.Graphics {
|
||||
}
|
||||
|
||||
parseResource(v) {
|
||||
const FACTORS = {
|
||||
'm': 1/1000,
|
||||
'Ki': 1000,
|
||||
'Mi': 1000 * 1000
|
||||
}
|
||||
const match = v.match(/^(\d*)(\D*)$/)
|
||||
const factor = FACTORS[match[2]] || 1
|
||||
return parseInt(match[1]) * factor
|
||||
}
|
||||
|
||||
hsvToRgb(h, s, v) {
|
||||
var r, g, b, i, f, p, q, t;
|
||||
i = Math.floor(h * 6);
|
||||
f = h * 6 - i;
|
||||
p = v * (1 - s);
|
||||
q = v * (1 - f * s);
|
||||
t = v * (1 - (1 - f) * s);
|
||||
switch (i % 6) {
|
||||
case 0:
|
||||
r = v, g = t, b = p; break;
|
||||
case 1:
|
||||
r = q, g = v, b = p; break;
|
||||
case 2:
|
||||
r = p, g = v, b = t; break;
|
||||
case 3:
|
||||
r = p, g = q, b = v; break;
|
||||
case 4:
|
||||
r = t, g = p, b = v; break;
|
||||
case 5:
|
||||
r = v, g = p, b = q; break;
|
||||
}
|
||||
return PIXI.utils.rgb2hex([r, g, b])
|
||||
}
|
||||
|
||||
getBarColor(usage, capacity) {
|
||||
return this.hsvToRgb(0.4 - (0.4 * (usage / capacity)), 0.6, 1)
|
||||
}
|
||||
|
||||
getResourceUsage() {
|
||||
const resources = {}
|
||||
for (var key of Object.keys(this.node.status.capacity)) {
|
||||
@@ -88,22 +128,24 @@ export default class Node extends PIXI.Graphics {
|
||||
})
|
||||
const resources = this.getResourceUsage()
|
||||
const cpuHeight = 80 / resources.cpu.capacity
|
||||
nodeBox.lineStyle(2, 0xaaffaa, 1)
|
||||
nodeBox.beginFill(0xaaffaa, 1)
|
||||
nodeBox.drawRect(3, 110 - resources.cpu.requested * cpuHeight, 3, resources.cpu.requested * cpuHeight)
|
||||
nodeBox.drawRect(5, 110 - resources.cpu.used * cpuHeight, 5, resources.cpu.used * cpuHeight)
|
||||
nodeBox.lineStyle(0, 0xaaffaa, 1)
|
||||
nodeBox.beginFill(this.getBarColor(resources.cpu.requested, resources.cpu.capacity), 1)
|
||||
nodeBox.drawRect(5, 110 - resources.cpu.requested * cpuHeight, 2.5, resources.cpu.requested * cpuHeight)
|
||||
nodeBox.beginFill(this.getBarColor(resources.cpu.used, resources.cpu.capacity), 1)
|
||||
nodeBox.drawRect(7.5, 110 - resources.cpu.used * cpuHeight, 2.5, resources.cpu.used * cpuHeight)
|
||||
nodeBox.endFill()
|
||||
nodeBox.lineStyle(2, 0xaaaaaa, 1);
|
||||
nodeBox.lineStyle(1, 0xaaaaaa, 1);
|
||||
for (var i=0; i<resources.cpu.capacity; i++) {
|
||||
nodeBox.drawRect(5, 110 - (i+1) * cpuHeight, 5, cpuHeight)
|
||||
}
|
||||
|
||||
const scale = resources.memory.capacity / 80
|
||||
nodeBox.drawRect(14, 110 - resources.memory.capacity/scale, 5, resources.memory.capacity/scale)
|
||||
nodeBox.lineStyle(2, 0xaaffaa, 1)
|
||||
nodeBox.beginFill(0xaaffaa, 1)
|
||||
nodeBox.drawRect(13, 110 - resources.memory.requested/scale, 3, resources.memory.requested/scale)
|
||||
nodeBox.drawRect(15, 110 - resources.memory.used/scale, 3, resources.memory.used/scale)
|
||||
nodeBox.lineStyle(0, 0xaaffaa, 1)
|
||||
nodeBox.beginFill(this.getBarColor(resources.memory.requested, resources.memory.capacity), 1)
|
||||
nodeBox.drawRect(14, 110 - resources.memory.requested/scale, 2.5, resources.memory.requested/scale)
|
||||
nodeBox.beginFill(this.getBarColor(resources.memory.used, resources.memory.capacity), 1)
|
||||
nodeBox.drawRect(16.5, 110 - resources.memory.used/scale, 2.5, resources.memory.used/scale)
|
||||
nodeBox.endFill()
|
||||
var text = new PIXI.Text('', {fontSize: 10, fill: 0xffffff})
|
||||
nodeBox.addChild(text)
|
||||
|
||||
@@ -15,7 +15,7 @@ module.exports = {
|
||||
output: {
|
||||
path: path.resolve(pkg.config.buildDir),
|
||||
publicPath: DEBUG ? "/" : "./",
|
||||
filename: "app.js"
|
||||
filename: DEBUG ? 'app.js' : 'app-[hash].js'
|
||||
},
|
||||
node: {
|
||||
fs: 'empty'
|
||||
@@ -33,4 +33,4 @@ module.exports = {
|
||||
loader: 'transform?brfs'
|
||||
}]
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
12
examples/unassigned-pod.yaml
Normal file
12
examples/unassigned-pod.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
# example Pod which cannot be assigned to any node
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: unassigned-test
|
||||
spec:
|
||||
# use a node selector which will never match..
|
||||
nodeSelector:
|
||||
stuff: x
|
||||
containers:
|
||||
- name: test
|
||||
image: foo
|
||||
@@ -6,6 +6,6 @@
|
||||
<style>* {padding: 0; margin: 0}</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="static/app.js"></script>
|
||||
<script src="static/{{ app_js }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user