@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
125 lines (124 loc) • 6.81 kB
JavaScript
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as jsyaml from 'js-yaml';
import { findMatchingContexts, handleDataBaseError, handleDatabaseUpgrade, } from '.';
/**
* deleteClusterKubeconfig deletes the kubeconfig for a stateless cluster from indexedDB
* @param clusterName - The name of the cluster
* @param clusterID The ID for a cluster, composed of the kubeconfig path and cluster name
* @returns A promise that resolves with the kubeconfig, or null if not found.
*/
export async function deleteClusterKubeconfig(clusterName, clusterID) {
return new Promise(async (resolve, reject) => {
try {
const request = indexedDB.open('kubeconfigs', 1);
// The onupgradeneeded event is fired when the database is created for the first time.
request.onupgradeneeded = handleDatabaseUpgrade;
// The onsuccess event is fired when the database is opened.
// This event is where you specify the actions to take when the database is opened.
request.onsuccess = function handleDatabaseSuccess(event) {
const db = event.target.result;
const transaction = db.transaction(['kubeconfigStore'], 'readwrite');
const store = transaction.objectStore('kubeconfigStore');
// The onsuccess event is fired when the request has succeeded.
// This is where you handle the results of the request.
// The result is the cursor. It is used to iterate through the object store.
// The cursor is null when there are no more objects to iterate through.
// The cursor is used to find the kubeconfig by cluster name.
store.openCursor().onsuccess = function storeSuccess(event) {
// delete the kubeconfig by cluster name
const successEvent = event;
const cursor = successEvent.target.result;
// when we do not find a cursor, we resolve with null
if (!cursor) {
resolve(null);
return;
}
const row = cursor.value;
const kubeconfig64 = row.kubeconfig;
const parsed = jsyaml.load(atob(kubeconfig64));
const { matchingKubeconfig, matchingContext } = findMatchingContexts(clusterName, parsed, clusterID);
// if neither a matching kubeconfig nor a matching context is found, continue to the next cursor
if (!matchingKubeconfig && !matchingContext) {
cursor.continue();
return;
}
// for matches found we compute the names we will delete
const contextName = matchingContext?.name ?? matchingKubeconfig?.name ?? undefined;
// remove only the matched context from the kubeconfig store
parsed.contexts = (parsed.contexts || []).filter(context => context.name !== contextName);
// if the context was the 'current-context' we just removed then we need to reset it
if (parsed['current-context'] === contextName) {
const nextContext = parsed.contexts && parsed.contexts[0]?.name;
if (nextContext) {
parsed['current-context'] = nextContext;
}
else {
delete parsed['current-context'];
}
}
// determine what clusters/users are still referenced by remaining contexts
const remainingRefs = new Set((parsed.contexts || []).flatMap(c => {
const arr = [];
if (c.context.cluster)
arr.push(c.context.cluster);
if (c.context.user)
arr.push(c.context.user);
return arr;
}));
// clean up unreferenced clusters/users
if (parsed.clusters) {
parsed.clusters = parsed.clusters.filter(c => remainingRefs.has(c.name));
}
if (parsed.users) {
parsed.users = parsed.users.filter(u => remainingRefs.has(u.name));
}
// if no contexts remain, delete the whole indexDB row
if (!parsed.contexts || parsed.contexts.length === 0) {
const deleteRequest = store.delete(cursor.key);
deleteRequest.onsuccess = () => {
console.debug('Kubeconfig deleted from IndexedDB');
resolve(kubeconfig64);
};
deleteRequest.onerror = () => {
console.error('Error deleting kubeconfig from IndexedDB');
reject('Error deleting kubeconfig from IndexedDB');
};
return;
}
// save the updated kubeconfig back to indexedDB
const updatedKubeconfig64 = btoa(jsyaml.dump(parsed));
const updatedRow = { ...row, kubeconfig: updatedKubeconfig64 };
const putRequest = store.put(updatedRow);
putRequest.onsuccess = () => {
console.debug('Kubeconfig updated in IndexedDB');
resolve(kubeconfig64);
};
putRequest.onerror = () => {
console.error('Error updating kubeconfig in IndexedDB');
reject('Error updating kubeconfig in IndexedDB');
};
};
};
// The onerror event is fired when the database is opened.
// This is where you handle errors.
request.onerror = handleDataBaseError;
}
catch (error) {
reject(error);
}
});
}