UNPKG

@stolostron/multicluster-sdk

Version:

Provides extensions and APIs that dynamic plugins can use to leverage multicluster capabilities provided by Red Hat Advanced Cluster Management.

125 lines 7.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useFleetK8sWatchResources = useFleetK8sWatchResources; /* Copyright Contributors to the Open Cluster Management project */ const useHubClusterName_1 = require("./useHubClusterName"); const dynamic_plugin_sdk_1 = require("@openshift-console/dynamic-plugin-sdk"); const useIsFleetAvailable_1 = require("./useIsFleetAvailable"); const react_1 = require("react"); const useDeepCompareMemoize_1 = require("../internal/hooks/useDeepCompareMemoize"); const fleetK8sWatchResource_1 = require("../internal/fleetK8sWatchResource"); const getFleetK8sAPIPath_1 = require("../internal/getFleetK8sAPIPath"); function getModelForWatch(watch, models) { if (watch.groupVersionKind) { const { group, version, kind } = watch.groupVersionKind; const reference = [group || 'core', version, kind].join('~'); return models[reference] ?? models[kind]; } else if (watch.kind) { const kind = watch.kind?.split('~')?.[2]; return models[watch.kind] ?? models[kind]; } return undefined; } /** * A hook for watching multiple Kubernetes resources with support for multi-cluster environments. * It is equivalent to the [`useK8sWatchResources`](https://github.com/openshift/console/blob/main/frontend/packages/console-dynamic-plugin-sdk/docs/api.md#usek8swatchresources) * hook from the [OpenShift Console Dynamic Plugin SDK](https://www.npmjs.com/package/@openshift-console/dynamic-plugin-sdk) * but allows you to retrieve data from any cluster managed by Red Hat Advanced Cluster Management. * * It automatically detects the hub cluster and handles resource watching on both hub * and remote clusters using WebSocket connections for real-time updates. * * @param initResources - An object where each key represents a resource identifier and each value is a resource watch configuration. Can be null to disable all watches. * @param initResources[key].cluster - The managed cluster on which the resource resides; null or undefined for the hub cluster * @param initResources[key].groupVersionKind - The group, version, and kind of the resource (e.g., `{ group: 'apps', version: 'v1', kind: 'Deployment' }`) * @param initResources[key].name - The name of the resource (for watching a specific resource) * @param initResources[key].namespace - The namespace of the resource * @param initResources[key].isList - Whether to watch a list of resources (true) or a single resource (false) * @param initResources[key].selector - Label selector to filter resources (e.g., `{ matchLabels: { app: 'myapp' } }`) * @param initResources[key].fieldSelector - Field selector to filter resources (e.g., `status.phase=Running`) * @param initResources[key].limit - Maximum number of resources to return (not supported yet) * @param initResources[key].namespaced - Whether the resource is namespaced (not supported yet) * @param initResources[key].optional - If true, errors will not be thrown when the resource is not found (not supported yet) * @param initResources[key].partialMetadata - If true, only fetch metadata for the resources (not supported yet) * @returns An object with the same keys as initResources, where each value contains the watched resource data, * a boolean indicating if the data is loaded, and any error that occurred. The hook returns live-updating data. * * @example * ```typescript * // Watch multiple resources on different clusters * const result = useFleetK8sWatchResources({ * pods: { * groupVersionKind: { version: 'v1', kind: 'Pod' }, * isList: true, * cluster: 'remote-cluster-1', * namespace: 'default' * }, * deployments: { * groupVersionKind: { group: 'apps', version: 'v1', kind: 'Deployment' }, * isList: true, * cluster: 'remote-cluster-2', * namespace: 'default' * } * }) * * // Access individual resources * const { pods, deployments } = result * console.log(pods.data, pods.loaded, pods.loadError) * console.log(deployments.data, deployments.loaded, deployments.loadError) * ``` */ function useFleetK8sWatchResources(initResources) { const isFleetAvailable = (0, useIsFleetAvailable_1.useIsFleetAvailable)(); const [hubClusterName, hubClusterNameLoaded] = (0, useHubClusterName_1.useHubClusterName)(); const [memoizedResources, memoizedResourceChanged] = (0, useDeepCompareMemoize_1.useDeepCompareMemoize)(initResources, true); const resources = memoizedResources ?? {}; const [models, modelsLoading] = (0, dynamic_plugin_sdk_1.useK8sModels)(); const waitingForHubClusterName = Object.values(resources).some((watch) => !!watch.cluster) && !hubClusterNameLoaded; const probablyHasFleetQueries = Object.values(resources).some((watch) => !!watch.cluster && watch.cluster !== hubClusterName); // avoid using the fleet query if it is not available // avoid using either query if we are still waiting for the hub name to compare against the supplied cluster name const useFleet = probablyHasFleetQueries && isFleetAvailable && !waitingForHubClusterName; const useLocal = !probablyHasFleetQueries && !waitingForHubClusterName; const isResourceNullOrEmpty = !initResources || Object.keys(initResources).length === 0; const getInitialResult = (0, fleetK8sWatchResource_1.useGetInitialResult)(); const initialFleetResult = {}; for (const [key, watch] of Object.entries(resources)) { initialFleetResult[key] = getInitialResult(watch, getModelForWatch(watch, models), (0, getFleetK8sAPIPath_1.getFleetK8sAPIPath)(hubClusterName, watch.cluster)); } const [fleetResult, setFleetResult] = (0, react_1.useState)(initialFleetResult); if (memoizedResourceChanged) { setFleetResult(initialFleetResult); } const localResult = (0, dynamic_plugin_sdk_1.useK8sWatchResources)(useLocal && !isResourceNullOrEmpty ? initResources : {}); (0, react_1.useEffect)(() => { if (useFleet && !isResourceNullOrEmpty && !modelsLoading) { const watches = []; for (const [key, watch] of Object.entries(resources)) { const model = getModelForWatch(watch, models); const path = (0, getFleetK8sAPIPath_1.getFleetK8sAPIPath)(hubClusterName, watch.cluster); const requestPath = model && path ? (0, fleetK8sWatchResource_1.getRequestPathFromResource)(watch, model, path) : ''; watches.push([ watch, model, path, (0, fleetK8sWatchResource_1.subscribe)(watch, requestPath, (newResult) => setFleetResult((result) => ({ ...result, [key]: newResult }))), ]); } for (const [watch, model, path] of watches) { (0, fleetK8sWatchResource_1.startWatch)(watch, model, path); } return () => { for (const [watch, model, path, unsubscribe] of watches) { unsubscribe(); (0, fleetK8sWatchResource_1.stopWatch)(watch, model, path); } }; } }, // models updates frequently, but does not require a re-render // eslint-disable-next-line react-hooks/exhaustive-deps [isResourceNullOrEmpty, resources, modelsLoading, useFleet, hubClusterName]); return useLocal ? localResult : fleetResult; } //# sourceMappingURL=useFleetK8sWatchResources.js.map