18
app.py
18
app.py
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import gevent.monkey
|
||||
|
||||
gevent.monkey.patch_all()
|
||||
|
||||
import flask
|
||||
@@ -183,7 +184,8 @@ def get_kubernetes_clusters():
|
||||
pods_by_namespace_name = {}
|
||||
unassigned_pods = []
|
||||
for node in response.json()['items']:
|
||||
obj = {'name': node['metadata']['name'], 'labels': node['metadata']['labels'], 'status': node['status'], 'pods': []}
|
||||
obj = {'name': node['metadata']['name'], 'labels': node['metadata']['labels'], 'status': node['status'],
|
||||
'pods': []}
|
||||
nodes.append(obj)
|
||||
nodes_by_name[obj['name']] = obj
|
||||
response = session.get(urljoin(api_server_url, '/api/v1/pods'), timeout=5)
|
||||
@@ -191,9 +193,15 @@ def get_kubernetes_clusters():
|
||||
for pod in response.json()['items']:
|
||||
obj = {'name': pod['metadata']['name'],
|
||||
'namespace': pod['metadata']['namespace'],
|
||||
'labels': pod['metadata'].get('labels', {}), 'status': pod['status'], 'containers': []}
|
||||
'labels': pod['metadata'].get('labels', {}),
|
||||
'status': pod['status'],
|
||||
'startTime': pod['status']['startTime'] if 'startTime' in pod['status'] else '',
|
||||
'containers': []
|
||||
}
|
||||
if 'deletionTimestamp' in pod['metadata']:
|
||||
obj['deleted'] = datetime.datetime.strptime(pod['metadata']['deletionTimestamp'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=datetime.timezone.utc).timestamp()
|
||||
obj['deleted'] = datetime.datetime.strptime(pod['metadata']['deletionTimestamp'],
|
||||
'%Y-%m-%dT%H:%M:%SZ').replace(
|
||||
tzinfo=datetime.timezone.utc).timestamp()
|
||||
for cont in pod['spec']['containers']:
|
||||
obj['containers'].append({'name': cont['name'], 'image': cont['image'], 'resources': cont['resources']})
|
||||
pods_by_namespace_name[(obj['namespace'], obj['name'])] = obj
|
||||
@@ -210,7 +218,9 @@ def get_kubernetes_clusters():
|
||||
except:
|
||||
logging.exception('Failed to get metrics')
|
||||
try:
|
||||
response = session.get(urljoin(api_server_url, '/api/v1/namespaces/kube-system/services/heapster/proxy/apis/metrics/v1alpha1/pods'), timeout=5)
|
||||
response = session.get(urljoin(api_server_url,
|
||||
'/api/v1/namespaces/kube-system/services/heapster/proxy/apis/metrics/v1alpha1/pods'),
|
||||
timeout=5)
|
||||
response.raise_for_status()
|
||||
for metrics in response.json()['items']:
|
||||
pod = pods_by_namespace_name.get((metrics['metadata']['namespace'], metrics['metadata']['name']))
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/hjacobs/kube-ops-view#readme",
|
||||
"dependencies": {
|
||||
"pixi-display": "^1.0.1",
|
||||
"pixi.js": "^4.3.0",
|
||||
"webpack-dev-server": "^1.16.2"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import Tooltip from './tooltip.js'
|
||||
import Cluster from './cluster.js'
|
||||
import { Pod, ALL_PODS } from './pod.js'
|
||||
import {Pod, ALL_PODS, sortByName, sortByMemory, sortByCPU, sortByAge} from './pod.js'
|
||||
import SelectBox from './selectbox'
|
||||
import { PRIMARY_VIOLET } from './colors.js'
|
||||
|
||||
const PIXI = require('pixi.js')
|
||||
|
||||
|
||||
export default class App {
|
||||
|
||||
constructor() {
|
||||
@@ -16,6 +19,7 @@ export default class App {
|
||||
this.seenPods = new Set()
|
||||
this.desaturationFilter = new PIXI.filters.ColorMatrixFilter()
|
||||
this.desaturationFilter.desaturate()
|
||||
this.sorterFn = ''
|
||||
}
|
||||
|
||||
filter() {
|
||||
@@ -42,7 +46,7 @@ export default class App {
|
||||
for (const pod of node.children) {
|
||||
const name = pod.pod && pod.pod.name
|
||||
if (name) {
|
||||
if (!name.includes(searchString)){
|
||||
if (!name.includes(searchString)) {
|
||||
pod.filters = [filter]
|
||||
} else {
|
||||
// TODO: pod might have other filters set..
|
||||
@@ -70,24 +74,46 @@ export default class App {
|
||||
|
||||
const menuBar = new PIXI.Graphics()
|
||||
menuBar.beginFill(PRIMARY_VIOLET, 1)
|
||||
menuBar.drawRect(0, 0, window.innerWidth, 25)
|
||||
menuBar.endFill()
|
||||
menuBar.drawRect(0, 0, window.innerWidth, 28)
|
||||
menuBar.lineStyle(1.5, 0x000000, 1)
|
||||
menuBar.drawRect(20, 3, 200, 22)
|
||||
stage.addChild(menuBar)
|
||||
|
||||
const searchPrompt = new PIXI.Text('>', {fontFamily: 'ShareTechMono', fontSize: 18})
|
||||
searchPrompt.x = 20
|
||||
searchPrompt.y = 5
|
||||
PIXI.ticker.shared.add(function(_) {
|
||||
var v = Math.sin((PIXI.ticker.shared.lastTime % 2000)/2000.* Math.PI)
|
||||
const searchPrompt = new PIXI.Text('>', {fontFamily: 'ShareTechMono', fontSize: 14})
|
||||
searchPrompt.x = 26
|
||||
searchPrompt.y = 8
|
||||
PIXI.ticker.shared.add(function (_) {
|
||||
var v = Math.sin((PIXI.ticker.shared.lastTime % 2000) / 2000. * Math.PI)
|
||||
searchPrompt.alpha = v
|
||||
})
|
||||
stage.addChild(searchPrompt)
|
||||
|
||||
const searchText = new PIXI.Text('', {fontFamily: 'ShareTechMono', fontSize: 18})
|
||||
const searchText = new PIXI.Text('', {fontFamily: 'ShareTechMono', fontSize: 14})
|
||||
searchText.x = 40
|
||||
searchText.y = 5
|
||||
searchText.y = 8
|
||||
stage.addChild(searchText)
|
||||
|
||||
const items = [
|
||||
{
|
||||
text: 'SORT: NAME', sorterFn: sortByName
|
||||
},
|
||||
{
|
||||
text: 'SORT: AGE', sorterFn: sortByAge
|
||||
},
|
||||
{
|
||||
text: 'SORT: MEMORY', sorterFn: sortByMemory
|
||||
},
|
||||
{
|
||||
text: 'SORT: CPU', sorterFn: sortByCPU
|
||||
}
|
||||
]
|
||||
//setting default sort
|
||||
App.sorterFn = items[0].sorterFn
|
||||
const selectBox = new SelectBox(items)
|
||||
selectBox.x = 265
|
||||
selectBox.y = 3
|
||||
menuBar.addChild(selectBox.draw())
|
||||
|
||||
const viewContainer = new PIXI.Container()
|
||||
viewContainer.x = 20
|
||||
viewContainer.y = 40
|
||||
@@ -104,7 +130,7 @@ export default class App {
|
||||
event.preventDefault()
|
||||
}
|
||||
else if (event.key == 'Backspace') {
|
||||
this.filterString = this.filterString.slice(0, Math.max(0, this.filterString.length-1))
|
||||
this.filterString = this.filterString.slice(0, Math.max(0, this.filterString.length - 1))
|
||||
this.filter()
|
||||
event.preventDefault()
|
||||
}
|
||||
@@ -138,7 +164,7 @@ export default class App {
|
||||
pod._progress = 0
|
||||
originalPod.visible = false
|
||||
const that = this
|
||||
const tick = function(t) {
|
||||
const tick = function (t) {
|
||||
// progress goes from 0 to 1
|
||||
const progress = Math.min(1, pod._progress + (0.01 * t))
|
||||
const scale = 1 + ((1 - progress) * 140)
|
||||
@@ -269,13 +295,13 @@ export default class App {
|
||||
|
||||
function fetchData() {
|
||||
fetch('kubernetes-clusters', {credentials: 'include'})
|
||||
.then(function(response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function(json) {
|
||||
const clusters = json.kubernetes_clusters
|
||||
that.update(clusters)
|
||||
})
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(function (json) {
|
||||
const clusters = json.kubernetes_clusters
|
||||
that.update(clusters)
|
||||
})
|
||||
window.setTimeout(fetchData, 5000)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {FACTORS, getBarColor} from './utils'
|
||||
import {PRIMARY_VIOLET} from './colors'
|
||||
|
||||
const PIXI = require('pixi.js')
|
||||
|
||||
@@ -15,10 +16,11 @@ export default class Bars extends PIXI.Graphics {
|
||||
|
||||
const barHeight = 92
|
||||
|
||||
bars.lineStyle(0, 0xaaffaa, 1)
|
||||
bars.beginFill(0x999999, 0.1)
|
||||
bars.beginFill(PRIMARY_VIOLET, 0.1)
|
||||
bars.drawRect(5, 110 - barHeight, 15, barHeight)
|
||||
bars.endFill()
|
||||
|
||||
// CPU
|
||||
const cpuHeight = barHeight / bars.resources.cpu.capacity
|
||||
bars.interactive = true
|
||||
bars.lineStyle(0, 0xaaffaa, 1)
|
||||
@@ -27,19 +29,23 @@ export default class Bars extends PIXI.Graphics {
|
||||
bars.beginFill(getBarColor(bars.resources.cpu.used, bars.resources.cpu.capacity), 1)
|
||||
bars.drawRect(7.5, 110 - bars.resources.cpu.used * cpuHeight, 2.5, bars.resources.cpu.used * cpuHeight)
|
||||
bars.endFill()
|
||||
bars.lineStyle(1, 0xaaaaaa, 1)
|
||||
for (var i = 0; i < bars.resources.cpu.capacity; i++) {
|
||||
bars.drawRect(5, 110 - (i + 1) * cpuHeight, 5, cpuHeight)
|
||||
}
|
||||
|
||||
// Memory
|
||||
const scale = bars.resources.memory.capacity / barHeight
|
||||
bars.drawRect(14, 110 - bars.resources.memory.capacity / scale, 5, bars.resources.memory.capacity / scale)
|
||||
bars.lineStyle(0, 0xaaffaa, 1)
|
||||
bars.beginFill(getBarColor(bars.resources.memory.requested, bars.resources.memory.capacity), 1)
|
||||
bars.drawRect(14, 110 - bars.resources.memory.requested / scale, 2.5, bars.resources.memory.requested / scale)
|
||||
bars.beginFill(getBarColor(bars.resources.memory.used, bars.resources.memory.capacity), 1)
|
||||
bars.drawRect(16.5, 110 - bars.resources.memory.used / scale, 2.5, bars.resources.memory.used / scale)
|
||||
bars.endFill()
|
||||
|
||||
bars.lineStyle(1, PRIMARY_VIOLET, 1)
|
||||
for (var i = 0; i < bars.resources.cpu.capacity; i++) {
|
||||
bars.drawRect(5, 110 - (i + 1) * cpuHeight, 5, cpuHeight)
|
||||
}
|
||||
|
||||
bars.drawRect(14, 110 - bars.resources.memory.capacity / scale, 5, bars.resources.memory.capacity / scale)
|
||||
|
||||
bars.on('mouseover', function () {
|
||||
let s = 'CPU: \n'
|
||||
const {capacity: cpuCap, requested: cpuReq, used: cpuUsed} = bars.resources.cpu
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {Pod} from './pod.js'
|
||||
import Bars from './bars.js'
|
||||
import {parseResource} from './utils.js'
|
||||
import {PRIMARY_VIOLET} from './colors.js'
|
||||
import App from './app'
|
||||
const PIXI = require('pixi.js')
|
||||
|
||||
export default class Node extends PIXI.Graphics {
|
||||
@@ -43,10 +45,10 @@ export default class Node extends PIXI.Graphics {
|
||||
return resources
|
||||
}
|
||||
|
||||
draw () {
|
||||
var nodeBox = this
|
||||
var topHandle = new PIXI.Graphics()
|
||||
topHandle.beginFill(0xaaaaff, 1)
|
||||
draw() {
|
||||
const nodeBox = this
|
||||
const topHandle = new PIXI.Graphics()
|
||||
topHandle.beginFill(PRIMARY_VIOLET, 1)
|
||||
topHandle.drawRect(0, 0, 105, 15)
|
||||
topHandle.endFill()
|
||||
const ellipsizedNodeName = this.node.name.substring(0, 18).concat('...')
|
||||
@@ -55,15 +57,15 @@ export default class Node extends PIXI.Graphics {
|
||||
text.y = 2
|
||||
topHandle.addChild(text)
|
||||
nodeBox.addChild(topHandle)
|
||||
nodeBox.lineStyle(2, 0xaaaaff, 1)
|
||||
nodeBox.beginFill(0x999999, 0.5)
|
||||
nodeBox.lineStyle(2, PRIMARY_VIOLET, 1)
|
||||
nodeBox.beginFill(PRIMARY_VIOLET, 0.2)
|
||||
nodeBox.drawRect(0, 0, 105, 115)
|
||||
nodeBox.endFill()
|
||||
nodeBox.lineStyle(2, 0xaaaaaa, 1)
|
||||
topHandle.interactive = true
|
||||
topHandle.on('mouseover', function () {
|
||||
var s = nodeBox.node.name
|
||||
for (var key of Object.keys(nodeBox.node.labels)) {
|
||||
let s = nodeBox.node.name
|
||||
for (const key of Object.keys(nodeBox.node.labels)) {
|
||||
s += '\n' + key + ': ' + nodeBox.node.labels[key]
|
||||
}
|
||||
nodeBox.tooltip.setText(s)
|
||||
@@ -79,9 +81,16 @@ export default class Node extends PIXI.Graphics {
|
||||
bars.y = 1
|
||||
nodeBox.addChild(bars.draw())
|
||||
|
||||
var px = 24
|
||||
var py = 20
|
||||
for (const pod of this.node.pods) {
|
||||
nodeBox.addPods(App.sorterFn)
|
||||
return nodeBox
|
||||
}
|
||||
|
||||
addPods(sorterFn) {
|
||||
const nodeBox = this
|
||||
let px = 24
|
||||
let py = 20
|
||||
const pods = sorterFn !== 'undefined' ? this.node.pods.sort(sorterFn) : this.node.pods
|
||||
for (const pod of pods) {
|
||||
if (pod.namespace != 'kube-system') {
|
||||
const podBox = Pod.getOrCreate(pod, this.cluster, this.tooltip)
|
||||
podBox.movePodTo(new PIXI.Point(px, py))
|
||||
@@ -92,11 +101,10 @@ export default class Node extends PIXI.Graphics {
|
||||
py += 13
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
px = 24
|
||||
py = 100
|
||||
for (const pod of this.node.pods) {
|
||||
for (const pod of pods) {
|
||||
if (pod.namespace == 'kube-system') {
|
||||
const podBox = Pod.getOrCreate(pod, this.cluster, this.tooltip)
|
||||
podBox.movePodTo(new PIXI.Point(px, py))
|
||||
@@ -107,9 +115,6 @@ export default class Node extends PIXI.Graphics {
|
||||
py -= 13
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nodeBox
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,36 @@
|
||||
const PIXI = require('pixi.js')
|
||||
import {FACTORS, parseResource, getBarColor} from './utils.js'
|
||||
import {FACTORS, getBarColor, podResource} from './utils.js'
|
||||
|
||||
export const ALL_PODS = {}
|
||||
const ALL_PODS = {}
|
||||
|
||||
const sortByName = (a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
}
|
||||
|
||||
const sortByAge = (a, b) => {
|
||||
const dateA = new Date(a.status.startTime)
|
||||
const dateB = new Date(b.status.startTime)
|
||||
if (dateA.getTime() < dateB.getTime()) {
|
||||
return -1
|
||||
} else if (dateA.getTime() === dateB.getTime())
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
}
|
||||
|
||||
const sortByMemory = (a, b) => {
|
||||
const aMem = podResource('memory')(a.containers, 'usage')
|
||||
const bMem = podResource('memory')(b.containers, 'usage')
|
||||
return bMem - aMem
|
||||
}
|
||||
|
||||
const sortByCPU = (a, b) => {
|
||||
const aCpu = podResource('cpu')(a.containers, 'usage')
|
||||
const bCpu = podResource('cpu')(b.containers, 'usage')
|
||||
return bCpu - aCpu
|
||||
}
|
||||
|
||||
export {ALL_PODS, sortByAge, sortByCPU, sortByMemory, sortByName}
|
||||
|
||||
export class Pod extends PIXI.Graphics {
|
||||
|
||||
@@ -54,13 +83,6 @@ export class Pod extends PIXI.Graphics {
|
||||
}
|
||||
|
||||
getResourceUsage() {
|
||||
const metric = (metric, type) =>
|
||||
metric ? (metric[type] ? parseResource(metric[type]) : 0) : 0
|
||||
|
||||
const podResource = type => (containers, resource) =>
|
||||
containers
|
||||
.map(({resources}) => metric(resources[resource], type))
|
||||
.reduce((a, b) => a + b, 0)
|
||||
|
||||
const podCpu = podResource('cpu')
|
||||
const podMem = podResource('memory')
|
||||
@@ -99,7 +121,7 @@ export class Pod extends PIXI.Graphics {
|
||||
}
|
||||
|
||||
pulsate(time) {
|
||||
const v = Math.sin((PIXI.ticker.shared.lastTime % 1000)/1000.* Math.PI)
|
||||
const v = Math.sin((PIXI.ticker.shared.lastTime % 1000) / 1000. * Math.PI)
|
||||
this.alpha = v * this._progress
|
||||
}
|
||||
|
||||
@@ -117,9 +139,9 @@ export class Pod extends PIXI.Graphics {
|
||||
|
||||
// pod.status.containerStatuses might be undefined!
|
||||
const containerStatuses = this.pod.status.containerStatuses || []
|
||||
var ready = 0
|
||||
var running = 0
|
||||
var restarts = 0
|
||||
let ready = 0
|
||||
let running = 0
|
||||
let restarts = 0
|
||||
for (const containerStatus of containerStatuses) {
|
||||
if (containerStatus.ready) {
|
||||
ready++
|
||||
@@ -133,17 +155,18 @@ export class Pod extends PIXI.Graphics {
|
||||
const allRunning = running >= containerStatuses.length
|
||||
const resources = this.getResourceUsage()
|
||||
|
||||
var newTick = null
|
||||
let newTick = null
|
||||
|
||||
const podBox = this
|
||||
podBox.interactive = true
|
||||
podBox.on('mouseover', function() {
|
||||
podBox.on('mouseover', function () {
|
||||
const filter = new PIXI.filters.ColorMatrixFilter()
|
||||
filter.brightness(1.3)
|
||||
podBox.filters = [filter]
|
||||
let s = this.pod.name
|
||||
s += '\nStatus: ' + this.pod.status.phase + ' (' + ready + '/' + containerStatuses.length + ' ready)'
|
||||
s += '\nLabels:'
|
||||
s += '\nStatus : ' + this.pod.status.phase + ' (' + ready + '/' + containerStatuses.length + ' ready)'
|
||||
s += '\nStart Time: ' + this.pod.status.startTime
|
||||
s += '\nLabels :'
|
||||
for (var key of Object.keys(this.pod.labels)) {
|
||||
if (key !== 'pod-template-hash') {
|
||||
s += '\n ' + key + ': ' + this.pod.labels[key]
|
||||
@@ -179,8 +202,8 @@ export class Pod extends PIXI.Graphics {
|
||||
this.tooltip.visible = false
|
||||
})
|
||||
podBox.lineStyle(2, 0xaaaaaa, 1)
|
||||
var i = 0
|
||||
var w = 10 / this.pod.containers.length
|
||||
let i = 0
|
||||
const w = 10 / this.pod.containers.length
|
||||
for (const container of this.pod.containers) {
|
||||
podBox.drawRect(i * w, 0, w, 10)
|
||||
i++
|
||||
@@ -225,9 +248,9 @@ export class Pod extends PIXI.Graphics {
|
||||
|
||||
if (restarts) {
|
||||
this.lineStyle(2, 0xff9999, 1)
|
||||
for (let i=0; i<Math.min(restarts, 4); i++) {
|
||||
this.moveTo(10, i*3 - 1)
|
||||
this.lineTo(10, i*3 + 1)
|
||||
for (let i = 0; i < Math.min(restarts, 4); i++) {
|
||||
this.moveTo(10, i * 3 - 1)
|
||||
this.lineTo(10, i * 3 + 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
86
app/src/selectbox.js
Normal file
86
app/src/selectbox.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import { PRIMARY_VIOLET } from './colors.js'
|
||||
import App from './app'
|
||||
|
||||
const PIXI = require('pixi.js')
|
||||
|
||||
export default class SelectBox extends PIXI.Graphics {
|
||||
constructor(items) {
|
||||
super()
|
||||
this.items = items
|
||||
this.count = 0
|
||||
this.text = new PIXI.Text(this.items[this.count].text, {
|
||||
fontFamily: 'ShareTechMono',
|
||||
fontSize: 14,
|
||||
fill: 0x000000,
|
||||
align: 'center'
|
||||
})
|
||||
this.text.x = 10
|
||||
this.text.y = 5
|
||||
this.addChild(this.text)
|
||||
}
|
||||
|
||||
onForwardPressed() {
|
||||
const selectBox = this
|
||||
selectBox.count++
|
||||
if (selectBox.count >= this.items.length) {
|
||||
selectBox.count = 0
|
||||
}
|
||||
selectBox.text.text = selectBox.items[selectBox.count].text
|
||||
App.sorterFn = selectBox.items[selectBox.count].sorterFn
|
||||
}
|
||||
|
||||
onBackPressed() {
|
||||
const selectBox = this
|
||||
selectBox.count--
|
||||
if (selectBox.count < 0) {
|
||||
selectBox.count = selectBox.items.length - 1
|
||||
}
|
||||
selectBox.text.text = selectBox.items[selectBox.count].text
|
||||
App.sorterFn = selectBox.items[selectBox.count].sorterFn
|
||||
}
|
||||
|
||||
draw() {
|
||||
const selectBox = this
|
||||
|
||||
const backArrow = new PIXI.Graphics()
|
||||
const forwardArrow = new PIXI.Graphics()
|
||||
backArrow.interactive = true
|
||||
forwardArrow.interactive = true
|
||||
selectBox.interactive = true
|
||||
|
||||
// draw a triangle
|
||||
backArrow.lineStyle(1.5, 0x000000, 1)
|
||||
backArrow.beginFill(PRIMARY_VIOLET, 0.9)
|
||||
backArrow.drawRect(-22, 0, 22, 22)
|
||||
backArrow.moveTo(-7, 6)
|
||||
backArrow.lineTo(-16, 11)
|
||||
backArrow.lineTo(-7, 16)
|
||||
backArrow.lineTo(-7, 6)
|
||||
backArrow.endFill()
|
||||
selectBox.addChild(backArrow)
|
||||
|
||||
selectBox.lineStyle(1.5, 0x000000, 1)
|
||||
selectBox.beginFill(PRIMARY_VIOLET, 0.5)
|
||||
selectBox.drawRect(4, 0, 100, 22)
|
||||
selectBox.endFill()
|
||||
|
||||
forwardArrow.lineStyle(1.5, 0x000000, 1)
|
||||
forwardArrow.beginFill(PRIMARY_VIOLET, 0.9)
|
||||
forwardArrow.drawRect(108, 0, 22, 22)
|
||||
forwardArrow.moveTo(115, 6)
|
||||
forwardArrow.lineTo(124, 11)
|
||||
forwardArrow.lineTo(115, 16)
|
||||
forwardArrow.lineTo(115, 6)
|
||||
forwardArrow.endFill()
|
||||
selectBox.addChild(forwardArrow)
|
||||
|
||||
backArrow.on('mousedown', selectBox.onBackPressed.bind(this))
|
||||
backArrow.on('touchstart', selectBox.onBackPressed.bind(this))
|
||||
forwardArrow.on('mousedown', selectBox.onForwardPressed.bind(this))
|
||||
forwardArrow.on('touchstart', selectBox.onForwardPressed.bind(this))
|
||||
|
||||
return selectBox
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,4 +68,12 @@ function parseResource(v) {
|
||||
return parseInt(match[1]) * factor
|
||||
}
|
||||
|
||||
export {FACTORS, hsvToRgb, getBarColor, parseResource}
|
||||
const metric = (metric, type) =>
|
||||
metric ? (metric[type] ? parseResource(metric[type]) : 0) : 0
|
||||
|
||||
const podResource = type => (containers, resource) =>
|
||||
containers
|
||||
.map(({resources}) => metric(resources[resource], type))
|
||||
.reduce((a, b) => a + b, 0)
|
||||
|
||||
export {FACTORS, hsvToRgb, getBarColor, parseResource, metric, podResource}
|
||||
|
||||
@@ -33,6 +33,7 @@ module.exports = {
|
||||
{test: /\.html$/, exclude: /node_modules/, loader: 'file-loader?name=[path][name].[ext]'},
|
||||
{test: /\.jpe?g$|\.svg$|\.png$/, exclude: /node_modules/, loader: 'file-loader?name=[path][name].[ext]'},
|
||||
{test: /\.json$/, exclude: /node_modules/, loader: 'json'},
|
||||
{test: /\.(otf|eot|svg|ttf|woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=8192&mimetype=application/font-woff'},
|
||||
{test: /\.json$/, include: path.join(__dirname, 'node_modules', 'pixi.js'), loader: 'json'}
|
||||
],
|
||||
postLoaders: [{
|
||||
|
||||
Reference in New Issue
Block a user