Skip to content

Commit

Permalink
Support for busola
Browse files Browse the repository at this point in the history
  • Loading branch information
pbochynski committed Nov 17, 2023
1 parent 223a75d commit 58bad73
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 59 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Open Web UI with this link: [http://127.0.0.1:8001/static/kyma.html](http://127.
If you don't have any cluster at hand you can use this playground:
[https://killercoda.com/interactive-kyma/scenario/oss-modules](https://killercoda.com/interactive-kyma/scenario/oss-modules)

## Usage

1. Apply - install module manager and default configuration
2. Delete - delete all manage resources including module configuration and when all of them are gone delete module manager deployment
3. Details - show resources included in the module manager deployment Yaml

## Contribute your module

Checkout the community-modules repository and add your own module by adding an entry in the [channels.json](app/channels.json) file. Example:
Expand All @@ -41,15 +47,17 @@ Checkout the community-modules repository and add your own module by adding an e
]
},
```
Mandatory fields:
Fields description:
- **name** - name of your module (keep it short)
- **deploymentYaml** - URL of your module deployment YAML (usually the artifact of your module release)
- **crYaml** - URL of your module default configuration (custom resource)
- **documentatio** - documentation URL
- **repository** - main source code repository
- **managedResources** - list of api server resources (paths) that are managed by your module (including the configuration resource)
- **base** - the name of the other channel that should be used as a base for fields not specified in current entry. Documentation, repository and managedResources usually are the same for all the channels, so you can use base reference instead of copying these values across all channels


The channels.json file is processed by the build process and generates release channels files (right now only latest release is generated: [https://kyma-project.github.io/community-modules/latest.json](https://kyma-project.github.io/community-modules/latest.json)
The channels.json file is processed by the build process and generates release channels files ([latest.json](https://kyma-project.github.io/community-modules/latest.json), [fast.json](https://kyma-project.github.io/community-modules/fast.json), and [regular.json](https://kyma-project.github.io/community-modules/regular.json))
If you want to test your module, you can generate that file on your own:
```
cd script
Expand Down
130 changes: 74 additions & 56 deletions app/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
var pods = []
var groupVersions = {}
const KUBECONFIG = { "kubernetes-admin@kubernetes": { "name": "kubernetes-admin@kubernetes", "kubeconfig": { "apiVersion": "v1", "clusters": [{ "cluster": { "server": "http://127.0.0.1:8001/backend" }, "name": "kyma-katacoda" }], "contexts": [{ "context": { "cluster": "kyma-katacoda", "user": "kubernetes-admin" }, "name": "kubernetes-admin@kubernetes" }], "current-context": "kubernetes-admin@kubernetes", "kind": "Config", "preferences": {}, "users": [{ "name": "kubernetes-admin", "user": { "token": "tokentokentoken" } }] }, "contextName": "kubernetes-admin@kubernetes", "config": { "storage": "sessionStorage" }, "currentContext": { "cluster": { "cluster": { "server": "http://127.0.0.1:8001/backend" }, "name": "kyma-katacoda" }, "user": { "name": "kubernetes-admin", "user": { "token": "tokentokentoken" } } } } }
var API_PREFIX = ''
const DEFAULT_CHANNEL = 'https://kyma-project.github.io/community-modules/latest.json'
const CHANNELS = [
{name:'regular', url:'https://kyma-project.github.io/community-modules/regular.json'},
{name:'fast',url:'https://kyma-project.github.io/community-modules/fast.json'},
{name:'latest',url:'https://kyma-project.github.io/community-modules/latest.json'}
{ name: 'regular', url: 'https://kyma-project.github.io/community-modules/regular.json' },
{ name: 'fast', url: 'https://kyma-project.github.io/community-modules/fast.json' },
{ name: 'latest', url: 'https://kyma-project.github.io/community-modules/latest.json' }
]
const KYMA_PATH = '/apis/operator.kyma-project.io/v1beta2/namespaces/kyma-system/kymas/default'

Expand All @@ -13,50 +15,62 @@ var modules = []
async function apply(res) {
let path = await resPath(res)
path += '?fieldManager=kubectl&fieldValidation=Strict&force=false'
let response = await fetch(path, { method: 'PATCH', headers: { 'content-type': 'application/apply-patch+yaml' }, body: JSON.stringify(res) })
let response = await fetch(API_PREFIX+path, { method: 'PATCH', headers: { 'content-type': 'application/apply-patch+yaml' }, body: JSON.stringify(res) })
return response
}
function channelDropdown() {
const url = new URL(window.location);
let channel = url.searchParams.get("channel") || DEFAULT_CHANNEL
let currentChannel = CHANNELS.find((ch)=>ch.url==channel)
let channelName=channel
API_PREFIX = url.searchParams.get("api") || ''
let currentChannel = CHANNELS.find((ch) => ch.url == channel)
let channelName = channel
if (currentChannel) {
channelName=currentChannel.name
channelName = currentChannel.name
}
let div = document.getElementById("topButtons")
div.innerHTML=""
div.innerHTML = ""
let a = document.createElement('a')
a.setAttribute('class','btn btn-secondary dropdown-toggle btn-sm')
a.setAttribute('role','button')
a.setAttribute('data-bs-toggle','dropdown')
a.setAttribute('aria-expanded','false')
a.textContent=channelName
a.setAttribute('class', 'btn btn-secondary dropdown-toggle btn-sm')
a.setAttribute('role', 'button')
a.setAttribute('data-bs-toggle', 'dropdown')
a.setAttribute('aria-expanded', 'false')
a.textContent = channelName
let ul = document.createElement('a')
ul.setAttribute('class','dropdown-menu')
ul.setAttribute('class', 'dropdown-menu')
for (let ch of CHANNELS) {
if (ch.url!=channel) {
if (ch.url != channel) {
let li = document.createElement('li')
let a = document.createElement('a')
a.setAttribute('class','dropdown-item')
a.textContent=ch.name
a.addEventListener('click',()=>{
a.setAttribute('class', 'dropdown-item')
a.textContent = ch.name
a.addEventListener('click', () => {
const url = new URL(window.location);
url.searchParams.set("channel", ch.url);
window.location=url
window.location = url
})
li.appendChild(a)
ul.appendChild(li)
}
}
let updateBtn = document.createElement('button')
updateBtn.setAttribute('class','btn btn-outline-primary btn-sm')
updateBtn.setAttribute('type','button')
updateBtn.textContent='Update status'
updateBtn.addEventListener('click',checkStatus)
updateBtn.setAttribute('class', 'btn btn-outline-primary btn-sm')
updateBtn.setAttribute('type', 'button')
updateBtn.textContent = 'Update status'
updateBtn.addEventListener('click', checkStatus)

div.appendChild(a)
div.appendChild(ul)
div.appendChild(updateBtn)

if (API_PREFIX!='') {
let busolaBtn = document.createElement('button')
busolaBtn.setAttribute('class', 'btn btn-outline-primary btn-sm')
busolaBtn.setAttribute('type', 'button')
busolaBtn.textContent = 'Kyma Dashboard'
busolaBtn.addEventListener('click', openBusola)
div.appendChild(busolaBtn)
}

return div
}

Expand All @@ -66,9 +80,13 @@ async function applyModule(m) {
}
await apply(m.cr.resource)
}
function openBusola() {
localStorage.setItem('busola.clusters','{"kyma":{"name":"kyma","kubeconfig":{"apiVersion":"v1","clusters":[{"cluster":{"server":"http://127.0.0.1:8001/backend"},"name":"kyma"}],"contexts":[{"context":{"cluster":"kyma","user":"admin"},"name":"kyma"}],"current-context":"kyma","kind":"Config","preferences":{},"users":[{"name":"admin","user":{"token":"tokentokentoken"}}]},"contextName":"kyma","config":{"storage":"localStorage"},"currentContext":{"cluster":{"cluster":{"server":"http://127.0.0.1:8001/backend"},"name":"kyma"},"user":{"name":"admin","user":{"token":"tokentokentoken"}}}}}')
window.open('/index.html','_blank')
}

function get(path) {
return fetch(path).then((res) => {
return fetch(API_PREFIX+path).then((res) => {
if (res.status == 200) {
return res.json()
}
Expand All @@ -80,16 +98,16 @@ function deleteModuleResources(m) {
if (r.path == '/api/v1/namespaces/kyma-system') {
continue; // skip kyma-system deletion
}
fetch(r.path, { method: 'DELETE' })
fetch(API_PREFIX+r.path, { method: 'DELETE' })
}
}
async function removeModuleFromKymaCR(name) {
let kyma = await get(KYMA_PATH)
if (kyma && kyma.spec.modules) {
for (let i=0;i<kyma.spec.modules.length;++i){
if (kyma.spec.modules[i].name ==name) {
for (let i = 0; i < kyma.spec.modules.length; ++i) {
if (kyma.spec.modules[i].name == name) {
let body = `[{"op":"remove","path":"/spec/modules/${i}"}]`
fetch(KYMA_PATH,{method:'PATCH',headers:{'content-type': 'application/json-patch+json'}, body})
fetch(API_PREFIX+KYMA_PATH, { method: 'PATCH', headers: { 'content-type': 'application/json-patch+json' }, body })
return
}
}
Expand All @@ -110,7 +128,7 @@ async function deleteModule(m) {
modal(body, "Delete confirmation", async () => {
for (let i = 0; i < 5 && toDelete.length > 0; ++i) {
for (let i of toDelete) {
fetch(i, { method: 'DELETE' })
fetch(API_PREFIX+i, { method: 'DELETE' })
}
toDelete = await managedResourcesList(m)
setTimeout(() => checkStatus(), 1000)
Expand Down Expand Up @@ -157,7 +175,7 @@ async function notManagedResources() {
for (let i of m.managedResources) {
managed.push(i)
}
}
}
}
return all.filter((r) => !managed.some((m) => m == r))
}
Expand Down Expand Up @@ -217,13 +235,13 @@ async function exists(path) {
if (!path) {
return false;
}
let response = await fetch(path)
let response = await fetch(API_PREFIX+path)
return (response.status == 200)
}

async function cacheAPI(apiVersion) {
let url = (apiVersion === 'v1') ? '/api/v1' : `/apis/${apiVersion}`
let res = await fetch(url)
let res = await fetch(API_PREFIX+url)
if (res.status == 200) {
let body = await res.json()
groupVersions[apiVersion] = body
Expand All @@ -242,7 +260,7 @@ function deploymentList(m) {
badge = `<span class="badge bg-success">applied</span>`
}
html += `<li class="list-group-item"><small>
<a href="${r.path}" class="text-decoration-none" target="_blank">${r.path}</a> ${badge}</small></li>`
<a href="${API_PREFIX+r.path}" class="text-decoration-none" target="_blank">${r.path}</a> ${badge}</small></li>`
}
div.innerHTML = html + '</ul>'
}
Expand Down Expand Up @@ -324,11 +342,11 @@ function moduleCard(m) {
let cardBody = document.createElement('div')
cardBody.setAttribute('class', 'card-body')
let txt = document.createElement("div")
let version = m.version.split(':')[m.version.split(':').length-1]
let version = m.version.split(':')[m.version.split(':').length - 1]
let html = `<h5>${m.name} ${moduleBadge(m)}</h5>
<small>
deployment: ${resourcesBadge(m)} ${availableBadge(m)} <br/>
<a href="${m.cr.path}" class="text-decoration-none" target="_blank">configuration</a> ${crBadge(m)}<br/>
<a href="${API_PREFIX+m.cr.path}" class="text-decoration-none" target="_blank">configuration</a> ${crBadge(m)}<br/>
version: ${version} ${versionBadge(m)}<br/>
<a href="${m.documentation}" class="text-decoration-none" target="_blank">docs <i class="bi bi-box-arrow-up-right"></i></a>
<a href="${m.repository}" class="text-decoration-none" target="_blank">repo <i class="bi bi-box-arrow-up-right"></i></a><br/>
Expand Down Expand Up @@ -358,11 +376,11 @@ function renderModules(m) {
}
}
}
function versionBadge(m){
function versionBadge(m) {
if (m.version == m.actualVersion) {
return `<span class="badge bg-success">ok</span>`
} else if (m.actualVersion) {
let v = m.actualVersion.split(':')[m.actualVersion.split(':').length-1]
let v = m.actualVersion.split(':')[m.actualVersion.split(':').length - 1]
return `<span class="badge bg-warning text-dark">applied: ${v}</span>`
}
return ""
Expand All @@ -372,21 +390,21 @@ function availableBadge(m) {
return `<span class="badge bg-success">Ready</span>`
} else if (m.actualVersion) {
return `<span class="badge bg-secondary">Not Ready</span>`
}
}
return ""
}
async function managedModules() {
let kyma = await get(KYMA_PATH)
if (kyma) {

for (let m of modules) {
if (m.name=='istio') {
m.managed=true
if (m.name == 'istio') {
m.managed = true
continue;
}
if (kyma.spec.modules) {
let mm = kyma.spec.modules.find((mod) => mod.name == m.name)
m.managed = (mm) ? true : false
m.managed = (mm) ? true : false
}
}
}
Expand Down Expand Up @@ -416,7 +434,7 @@ function checkStatus() {
for (let m of modules) {
resPath(m.cr.resource).then((p) => {
m.cr.path = p
return (p) ? fetch(p) : null
return (p) ? fetch(API_PREFIX+p) : null
}).then((res) => {
if (res) {
m.cr.status = (res.status == 200)
Expand All @@ -430,10 +448,10 @@ function checkStatus() {
})

for (let r of m.resources) {
m.available=false
m.actualVersion=undefined
m.available = false
m.actualVersion = undefined
if (r.path) {
fetch(r.path).then((res) => {
fetch(API_PREFIX+r.path).then((res) => {
if (res.status == 200) {
r.status = true
return res.json()
Expand All @@ -445,16 +463,16 @@ function checkStatus() {
}).then((json) => {
r.value = json
if (json && json.kind == 'Deployment') {
m.actualVersion = json.spec.template.spec.containers[0].image
console.log(m.actualVersion, json.status)
if (json.status && json.status.conditions) {
let av = json.status.conditions.find (c=>c.type=='Available')
if (av && av.status=="True") {
m.available=true
}
m.actualVersion = json.spec.template.spec.containers[0].image
console.log(m.actualVersion, json.status)
if (json.status && json.status.conditions) {
let av = json.status.conditions.find(c => c.type == 'Available')
if (av && av.status == "True") {
m.available = true
}
}
}

}).finally(() => {
renderModules(m)
})
Expand All @@ -464,7 +482,7 @@ function checkStatus() {
}

function getPods() {
fetch('/api/v1/pods')
fetch(API_PREFIX+'/api/v1/pods')
.then((response) => response.json())
.then((podList) => {
pods = podList.items.sort((a, b) => {
Expand Down Expand Up @@ -492,5 +510,5 @@ function renderNotManagedResources(list) {

channelDropdown()
loadChannel()
// .then(notManagedResources)
// .then(renderNotManagedResources)
// .then(notManagedResources)
// .then(renderNotManagedResources)
2 changes: 1 addition & 1 deletion app/kyma.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<div class="container-fluid bg-dark">
<nav class="navbar navbar-dark bg-dark">
<svg width="92" height="25" xmlns="http://www.w3.org/2000/svg"><g fill="#FFFFFF" fill-rule="nonzero"><path d="M34.3833 20.325V4.5h3.45v6.5417L43.0667 4.5h4.0083l-5.3 6.1833 5.8333 9.6417H43.75l-4.2083-6.9917-1.6667 1.95V20.35z"/><path d="M47.3667 24.275l.7083-2.6083c.1583.0416.4333.1083.8333.1833a6.1667 6.1667 0 0 0 1.325.125c.313.0144.623-.0639.8917-.225a1.5333 1.5333 0 0 0 .5417-.7667l.2166-.5583-4.3333-11.5h3.5083l2.5 7.4 2.5-7.4H59.35l-4.725 12.6a8.425 8.425 0 0 1-.6417 1.3167 4.4 4.4 0 0 1-.8333 1c-.3241.28-.701.4925-1.1083.625a4.4 4.4 0 0 1-1.475.2166 11.5833 11.5833 0 0 1-2.0167-.1416c-.4917-.0917-.8833-.1834-1.1833-.2667zM61.2583 20.325v-11.4h3.4v1.7833a4.9167 4.9167 0 0 1 .6084-.6833 5.4583 5.4583 0 0 1 .8333-.65 4.4667 4.4667 0 0 1 1.025-.4833A3.65 3.65 0 0 1 68.3583 8.7a3.55 3.55 0 0 1 2.1334.5333 2.5 2.5 0 0 1 .95 1.475c.175-.2083.3833-.4333.6166-.675.2503-.2522.53-.4732.8334-.6583a4.7 4.7 0 0 1 1.0333-.4833A3.6667 3.6667 0 0 1 75.1667 8.7a3.9333 3.9333 0 0 1 1.4583.25 2.825 2.825 0 0 1 1.0333.725c.2784.3213.4832.6996.6 1.1083.1358.454.2004.9263.1917 1.4v8.1417h-3.4167v-7.2417c.0233-.406-.06-.811-.2416-1.175a1.0667 1.0667 0 0 0-.9834-.3916 2.1917 2.1917 0 0 0-1.1583.325 7.425 7.425 0 0 0-1.0917.8333v7.6667h-3.4v-7.2584c.0233-.406-.06-.811-.2416-1.175a1.075 1.075 0 0 0-.9834-.3916 2.225 2.225 0 0 0-1.175.325 7.5 7.5 0 0 0-1.1.8333v7.6667l-3.4-.0167zM80.825 17.4083a3.4583 3.4583 0 0 1 1.6667-3.0666 12.0083 12.0083 0 0 1 5.4166-1.4167v-.275a1.4 1.4 0 0 0-.3416-1 1.5333 1.5333 0 0 0-1.1667-.375 5.125 5.125 0 0 0-2 .3917 7.275 7.275 0 0 0-1.6667 1.025l-1.4416-1.975a8.5 8.5 0 0 1 .975-.7167 7.6167 7.6167 0 0 1 1.2166-.65 7.9333 7.9333 0 0 1 1.5334-.4583 10.2333 10.2333 0 0 1 1.9083-.1667 4.275 4.275 0 0 1 3.3333 1.1333 4.85 4.85 0 0 1 1.0167 3.3334v4.3833c-.007.5563.0152 1.1127.0667 1.6667.0338.3734.1179.7407.25 1.0916h-3.3834c-.0666-.25-.125-.5-.175-.775a4.2917 4.2917 0 0 1-.0833-.9A5.3833 5.3833 0 0 1 86.2333 20a4.8083 4.8083 0 0 1-2.2.475 3.6333 3.6333 0 0 1-1.2-.1917 3.1417 3.1417 0 0 1-1.025-.5916 2.6917 2.6917 0 0 1-.6916-.975 3.2417 3.2417 0 0 1-.2917-1.3084zm3.3333-.5916a1 1 0 0 0 .3417.8333c.2607.1892.5783.2833.9.2667a3.8333 3.8333 0 0 0 1.525-.275 3.2083 3.2083 0 0 0 1.0417-.7167v-2.1083a7.975 7.975 0 0 0-2.9167.7083 1.4833 1.4833 0 0 0-.9083 1.2917h.0166zM0 5v9.6a4.575 4.575 0 0 1 2.0833-.5h2.0834a11.95 11.95 0 0 0 7.6166-2.5c-.55-.5-1.0916-1-1.6166-1.5C7.5 7.6083 4.775 5.2417 0 5z"/><path d="M25.5083 5.1167c-4.675.225-7.175 2.5666-9.7666 5.0416C12.9 12.8583 9.9083 15.65 4.125 15.65H2.0833a2.0583 2.0583 0 0 0-1.8166 1.0917 2.1167 2.1167 0 0 0-.2667.975v2.6166h25.5l.0083-15.2166zM12.2583 4.1667C10.0833 2.075 7.9 0 3.55 0v4.1667a13.8 13.8 0 0 1 5 2.6916 13.275 13.275 0 0 0 3.7083-2.6916z"/><path d="M21.2833.7167c-3.6166.2083-5.525 2.0333-7.5 3.9583A18.0417 18.0417 0 0 1 9.775 7.8167c.4583.4166.9167.8333 1.3583 1.275.4417.4416 1.1584 1.0916 1.7584 1.6166.6166-.5333 1.2166-1.1 1.825-1.6666 1.8833-1.7917 3.825-3.6 6.6083-4.6334L21.2833.7167z"/></g></svg>
<a class="navbar-brand" href="#">modules 2023-11-16 15:22</a>
<a class="navbar-brand" href="#">modules 2023-11-17 15:03</a>
</nav>

</div>
Expand Down

0 comments on commit 58bad73

Please sign in to comment.