UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

100 lines (99 loc) 4.94 kB
/* * 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 '.'; /** * Update the kubeconfig context extensions in IndexedDB. * @param kubeconfig - The kubeconfig to store. * @param customName - The custom name for the context extension. * @param clusterName - The name of the cluster to update the kubeconfig context for. * @returns promise that resolves when the kubeconfig is successfully updated and stored. * @throws Error if IndexedDB is not supported. * @throws Error if the kubeconfig is invalid. */ export function updateStatelessClusterKubeconfig(kubeconfig, customName, clusterName) { return new Promise(async (resolve, reject) => { try { const request = indexedDB.open('kubeconfigs', 1); request.onupgradeneeded = handleDatabaseUpgrade; // scan each row until we find the context with the clusterName request.onsuccess = function handleDatabaseSuccess(event) { const db = event.target.result; if (!db) { reject('Failed to open IndexedDB'); return; } const transaction = db.transaction(['kubeconfigStore'], 'readwrite'); const store = transaction.objectStore('kubeconfigStore'); // variable to track if we found and updated the context let updated = false; store.openCursor().onsuccess = function onCursor(event) { const e = event; const cursor = e.target?.result; if (!cursor) { if (!updated) { reject(`Cluster with name ${clusterName} not found`); } return; } const row = cursor.value; const rowKubeconfig64 = row.kubeconfig; const parsed = jsyaml.load(atob(rowKubeconfig64)); const { matchingKubeconfig, matchingContext } = findMatchingContexts(clusterName, parsed); // if no matching kubeconfig or context, continue to next cursor if (!matchingKubeconfig && !matchingContext) { cursor.continue(); return; } // we found a matching context or kubeconfig, update it const target = matchingContext ?? matchingKubeconfig; const extensions = target.context.extensions || []; let headlampExtensions = extensions.find(ext => ext.name === 'headlamp_info'); if (!headlampExtensions) { headlampExtensions = { name: 'headlamp_info', extension: { customName } }; extensions.push(headlampExtensions); } else { // we overwrite the new customName headlampExtensions.extension = { ...headlampExtensions.extension, customName }; } target.context.extensions = extensions; // now we need to modify the yaml back into this row only const updatedKubeconfig = btoa(jsyaml.dump(parsed)); const updatedRow = { ...row, kubeconfig: updatedKubeconfig }; const putRequest = store.put(updatedRow); putRequest.onsuccess = () => { updated = true; resolve(); }; putRequest.onerror = (err) => { reject(`Failed to update kubeconfig: ${err.target.error}`); }; store.openCursor().onerror = function onCursorError(event) { const de = event; reject(de.target ? de.target.error : 'Error during the cursor operation'); }; }; }; // The onerror event is fired when the database is opened. // This is where you handle errors request.onerror = handleDataBaseError; } catch (error) { reject(error); } }); }