UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

124 lines (123 loc) 5.22 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 jsyaml from 'js-yaml'; import * as statelessFunctions from '../stateless'; import { isBackstage } from './isBackstage'; // BACKSTAGE_TOKEN_STORAGE_KEY is the key used to store the backstage token in the local storage const BACKSTAGE_TOKEN_STORAGE_KEY = 'backstage_token'; /** * setBackstageToken sets the backstage token in the local storage * * @param token - the token to set */ function setBackstageToken(token) { if (typeof localStorage !== 'undefined') { localStorage.setItem(BACKSTAGE_TOKEN_STORAGE_KEY, token); } } /** * getBackstageToken gets the backstage token from the local storage * * @returns the backstage token */ export function getBackstageToken() { if (typeof localStorage !== 'undefined') { return localStorage.getItem(BACKSTAGE_TOKEN_STORAGE_KEY); } return null; } /** * storeKubeconfigFromBackstage stores the kubeconfig from backstage as a stateless cluster kubeconfig * * @param kubeconfig - the kubeconfig to store */ async function storeKubeconfigFromBackstage(kubeconfig) { try { // Decode base64 kubeconfig const decodedKubeconfig = atob(kubeconfig); const parsedKubeconfig = jsyaml.load(decodedKubeconfig); // For each context, create a new kubeconfig const promises = parsedKubeconfig.contexts.map(async (context) => { // Find the corresponding cluster and auth info const cluster = parsedKubeconfig.clusters.find((c) => c.name === context.context.cluster); const authInfo = parsedKubeconfig.users.find((u) => u.name === context.context.user); if (!cluster || !authInfo) { console.warn(`Missing cluster or auth info for context ${context.name}`); return; } // Create a new kubeconfig with just this context, cluster, and auth info const newKubeconfig = { apiVersion: parsedKubeconfig.apiVersion, kind: parsedKubeconfig.kind, preferences: parsedKubeconfig.preferences, 'current-context': context.name, contexts: [context], clusters: [cluster], users: [authInfo], }; // Convert back to YAML and base64 encode const newKubeconfigYaml = jsyaml.dump(newKubeconfig, { lineWidth: -1 }); const newKubeconfigBase64 = btoa(newKubeconfigYaml); await statelessFunctions.findAndReplaceKubeconfig(context.name, newKubeconfigBase64, true); }); // Wait for all kubeconfig operations to complete await Promise.all(promises); } catch (error) { console.error('Error storing kubeconfig from backstage:', error); } } const BACKSTAGE_ACK_TIMEOUT_MS = 1000; /** * setupBackstageMessageReceiver sets up a listener for messages from the backstage app * and sets the backend token if it is received * * @returns void */ export function setupBackstageMessageReceiver() { if (isBackstage()) { const handleMessage = async (event) => { try { const { type, payload } = event.data; if (type === 'BACKSTAGE_AUTH_TOKEN') { const { token } = payload || {}; if (token) { setBackstageToken(token); // send acknowledgement message back to parent after a timeout to ensure the parent is ready to receive the acknowledgement. setTimeout(() => { window.parent.postMessage({ type: 'BACKSTAGE_AUTH_TOKEN_ACK' }, '*'); }, BACKSTAGE_ACK_TIMEOUT_MS); } } else if (type === 'BACKSTAGE_KUBECONFIG') { const kubeconfig = payload?.kubeconfig; if (kubeconfig) { // set the stateless cluster kubeconfig await storeKubeconfigFromBackstage(kubeconfig); // send acknowledgement message back to parent after a timeout to ensure the parent is ready to receive the acknowledgement. setTimeout(() => { window.parent.postMessage({ type: 'BACKSTAGE_KUBECONFIG_ACK' }, '*'); }, BACKSTAGE_ACK_TIMEOUT_MS); } } } catch (error) { console.error('Error processing backstage message:', error); } }; window.addEventListener('message', handleMessage); } }