web-components/ccp/js/infrastructurelistcontrolle...

257 lines
7.2 KiB
JavaScript

class CCPInfrastructureList extends HTMLElement{
#boot;
#socket;
#interval = null;
#infrastructures;
#runtimes;
#rootdoc;
#style = `
<link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
.ccp_infrastructure_controller{
}
.ccp_toolbar_button {
padding: .3rem;
line-height: 1rem;
}
.ccp_infrastructure_list{
list-style:none;
padding-left: 1rem;
user-select: none;
}
.ccp_infrastructure_item{
padding: .3rem 0 .3rem 0;
}
.ccp_infrastructure_list .lxd{
background-color: #dd4814;
}
.ccp_infrastructure_list .docker{
background-color: #2496ed;
}
.ccp_runtime_list{
list-style:none;
padding-left: 1rem;
}
.ccp_runtime_item{
padding: .3rem 0 .3rem 0;
}
.ccp_instance_list{
list-style:none;
}
.ccp_instance_item{
padding: 0.3rem 0 0.3rem 1rem;
font-style: italic;
color: 888888;
}
</style>
`;
#serviceurl;
#broadcasturl;
constructor(){
super()
this.#boot = document.querySelector("d4s-boot-2")
this.#serviceurl = this.getAttribute("serviceurl")
this.#broadcasturl = this.getAttribute("broadcasturl")
if(!this.#broadcasturl){
this.#broadcasturl = this.#serviceurl.replace(/^http/, "ws")
}
this.#broadcasturl = this.#broadcasturl + "/ws/notification"
this.#rootdoc = this.attachShadow({ "mode" : "open"})
}
connectedCallback(){
this.connectBroadcast()
this.fetchInfrastructures()
}
fetchInfrastructures(){
const prom1 = this.#boot.secureFetch(this.#serviceurl + "/infrastructures/cache").
then(resp=>{
if(resp.status !== 200) throw "Unable to fetch infrastructure cache";
return resp.json()
}).then(data=>{
this.#infrastructures = data
this.showList()
}).catch(err=>{
alert(err)
})
/*const prom2 = this.#boot.secureFetch(this.#serviceurl + "/runtimes").
then(resp=>{
if(resp.status !== 200) throw "Unable to fetch runtimes";
return resp.json()
}).then(data=>{
this.#runtimes = data
}).catch(err=>{
alert(err)
})
Promise.all([prom1, prom2]).then(results=>{
this.showList()
})*/
}
refreshInfrastructures(){
this.#boot.secureFetch(this.#serviceurl + "/infrastructures/cache", { method : "HEAD"})
}
showList(){
if(this.getAttribute("render") !== "true") {
this.#rootdoc.innerHTML = ""
return;
}
this.#rootdoc.innerHTML = `
<div class="ccp_infrastructure_controller">
${this.#style}
${this.toolbar()}
<ul class="ccp_infrastructure_list">
${this.showInfrastructures()}
</ul>
</div>
`
this.#rootdoc.querySelector(".ccp_infrastructure_toolbar").addEventListener("click", ev=>{
const evname = ev.target.getAttribute("name")
switch(evname){
case "refresh":
this.refreshInfrastructures();
break;
}
})
const forms = Array.prototype.slice.call(this.#rootdoc.querySelectorAll(".ccp_runtime_builder"))
forms.forEach(f=>{
f.addEventListener("submit", ev=>{
ev.stopPropagation()
ev.preventDefault()
const sel = ev.target.querySelector("select")
let url = ev.target.action + sel.value
this.#boot.secureFetch(url, { method : ev.target.method})
return false
})
})
}
toolbar(){
return `
<div class="ccp_infrastructure_toolbar">
<button name="refresh" class="btn btn-primary ccp_toolbar_button" title="Refresh">&#8634;</button>
</div>
`
}
showInfrastructures(){
return this.#infrastructures.map(i => {
return `
<li class="ccp_infrastructure_item" title="${i.description}">
<details>
<summary>
<span>${i.name}</span>
${this.showAge(i)}
${this.showType(i)}
</summary>
${this.showDeployableRuntimes(i)}
<ul class="ccp_runtime_list">
${this.showRuntimes(i)}
</ul>
</details>
</li>
`
}).join("\n")
}
showDeployableRuntimes(infra){
const alreadydeployed = infra.runtimes.map(r=>r["descriptor-id"])
infra.available_runtimes = infra.available_runtimes ? infra.available_runtimes : []
const options = infra.available_runtimes.filter(r=>{
return infra.type === r.type && alreadydeployed.indexOf(r.id) === -1
}).map(r=>{
return `<option value="${r.name}" title="${r.description}">${r.name}</option>`
}).join("\n")
return `
<div style="padding-left:1rem">
<form class="ccp_runtime_builder" action="${this.#serviceurl}/infrastructures/${infra.id}/runtime/" method="POST">
<select name="runtime_selector" style="padding:.2rem;margin:5px;border:none">
${options}
</select>
<button type="submit" class="btn btn-primary ccp_toolbar_button" title="Deploy runtime">+</button>
</form>
</div>
`
}
showRuntimes(infra){
return infra.runtimes.map(r =>{
return `
<li class="ccp_runtime_item" title="${r.name}">
<details>
<summary>${r.name}</summary>
<ul class="ccp_instance_list">
${this.showInstances(r)}
</ul>
</details>
</li>
`
}).join("\n")
}
showInstances(runtime){
return runtime.instances.map(i =>{
return `
<li class="ccp_instance_item">${i.name}</li>
`
}).join("\n")
}
showAge(infra){
const age = Math.floor(((new Date()) - (new Date(infra.date))) / 3600000)
var cls = "badge-success"
var agetext = "fresh"
if(age > 24){
cls = "badge-warning";
agetext = "aged"
} else if (age > 72){
cls = "badge-secondary"
agetext = "old"
}
return `<span class="badge ${cls}" name="age" title="${age} hours">${agetext}</span>`
}
showType(infra){
return `<span class="badge badge-secondary ${infra.type}" title="${infra.type}">${infra.type}</span>`
}
connectBroadcast(){
this.#socket = new WebSocket(this.#broadcasturl + "/infrastructures");
this.#socket.onmessage = event=>{
this.fetchInfrastructures()
}
this.#interval = window.setInterval( ()=>{
if(this.#socket.readyState === 3){
this.#socket.close()
window.clearInterval(this.#interval)
this.connectBroadcast()
}else{
this.#socket.send("ping")
}
}, 30000)
}
getCompatibleRuntimes(rts){
const available = Object.keys(this.#infrastructures).reduce((acc, i) => {
const compatiblerts = this.#infrastructures[i].runtimes.
filter(r=>rts.indexOf(r["descriptor-id"]) >= 0).
map(cr=>{ return { runtime : cr, infrastructure : this.#infrastructures[i] } })
return acc.concat(compatiblerts)
}, [])
return available
}
}
window.customElements.define('d4s-ccp-infrastructurelist', CCPInfrastructureList);