UNPKG

@openshift-console/dynamic-plugin-sdk

Version:

Provides core APIs, types and utilities used by dynamic plugins at runtime.

181 lines (180 loc) 6.32 kB
import { Map as ImmutableMap } from 'immutable'; import * as _ from 'lodash'; import { k8sBasePath } from './k8s'; import { getReferenceForModel } from './k8s-ref'; import { WSFactory } from './ws-factory'; // eslint-disable-next-line const staticModels = require('@console/internal/models'); const getK8sAPIPath = ({ apiGroup = 'core', apiVersion }) => { const isLegacy = apiGroup === 'core' && apiVersion === 'v1'; let p = isLegacy ? '/api/' : '/apis/'; if (!isLegacy && apiGroup) { p += `${apiGroup}/`; } p += apiVersion; return p; }; export const getK8sResourcePath = (model, options) => { let u = getK8sAPIPath(model); if (options.ns) { u += `/namespaces/${options.ns}`; } u += `/${model.plural}`; if (options.name) { // Some resources like Users can have special characters in the name. u += `/${encodeURIComponent(options.name)}`; } if (options.path) { u += `/${options.path}`; } if (!_.isEmpty(options.queryParams)) { const q = _.map(options.queryParams, (v, k) => { return `${k}=${v}`; }); u += `?${q.join('&')}`; } return u; }; export const resourceURL = (model, options) => `${k8sBasePath}${getK8sResourcePath(model, options)}`; const toArray = (value) => (Array.isArray(value) ? value : [value]); export const requirementToString = (requirement) => { if (requirement.operator === 'Equals') { return `${requirement.key}=${requirement.values[0]}`; } if (requirement.operator === 'NotEquals') { return `${requirement.key}!=${requirement.values[0]}`; } if (requirement.operator === 'Exists') { return requirement.key; } if (requirement.operator === 'DoesNotExist') { return `!${requirement.key}`; } if (requirement.operator === 'In') { return `${requirement.key} in (${toArray(requirement.values).join(',')})`; } if (requirement.operator === 'NotIn') { return `${requirement.key} notin (${toArray(requirement.values).join(',')})`; } if (requirement.operator === 'GreaterThan') { return `${requirement.key} > ${requirement.values[0]}`; } if (requirement.operator === 'LessThan') { return `${requirement.key} < ${requirement.values[0]}`; } return ''; }; export const createEquals = (key, value) => ({ key, operator: 'Equals', values: [value], }); const isOldFormat = (selector) => !selector.matchLabels && !selector.matchExpressions; export const toRequirements = (selector = {}) => { const requirements = []; const matchLabels = isOldFormat(selector) ? selector : selector.matchLabels; const { matchExpressions } = selector; Object.keys(matchLabels || {}) .sort() .forEach((k) => { requirements.push(createEquals(k, matchLabels[k])); }); (matchExpressions || []).forEach((me) => { requirements.push(me); }); return requirements; }; export const selectorToString = (selector) => { const requirements = toRequirements(selector); return requirements.map(requirementToString).join(','); }; export const k8sWatch = (kind, query = {}, wsOptions = {}) => { const queryParams = { watch: 'true' }; const opts = { queryParams }; const wsOptionsUpdated = Object.assign({ host: 'auto', reconnect: true, jsonParse: true, bufferFlushInterval: 500, bufferMax: 1000, }, wsOptions); const { labelSelector } = query; if (labelSelector) { const encodedSelector = encodeURIComponent(selectorToString(labelSelector)); if (encodedSelector) { queryParams.labelSelector = encodedSelector; } } if (query.fieldSelector) { queryParams.fieldSelector = encodeURIComponent(query.fieldSelector); } if (query.ns) { opts.ns = query.ns; } if (query.resourceVersion) { queryParams.resourceVersion = encodeURIComponent(query.resourceVersion); } const path = resourceURL(kind, opts); wsOptionsUpdated.path = path; return new WSFactory(path, wsOptionsUpdated); }; let pluginStore; export const setPluginStore = (store) => { pluginStore = store; }; const modelKey = (model) => { // TODO: Use `referenceForModel` even for known API objects return model.crd ? getReferenceForModel(model) : model.kind; }; export const modelsToMap = (models) => { return ImmutableMap().withMutations((map) => { models.forEach((model) => map.set(modelKey(model), model)); }); }; export const isModelDefinition = (e) => { return e.type === 'ModelDefinition'; }; /** * Contains static resource definitions for Kubernetes objects. * Keys are of type `group:version:Kind`, but TypeScript doesn't support regex types (https://github.com/Microsoft/TypeScript/issues/6579). */ let k8sModels; const getK8sModels = () => { if (!k8sModels) { // TODO this was migrated from console and is only used for the fallback API discovery and can likely be removed k8sModels = modelsToMap(_.values(staticModels)); const hasModel = (model) => k8sModels.has(modelKey(model)); k8sModels = k8sModels.withMutations((map) => { const pluginModels = _.flatMap(pluginStore .getExtensionsInUse() .filter(isModelDefinition) .map((md) => md.properties.models)); map.merge(modelsToMap(pluginModels.filter((model) => !hasModel(model)))); }); } return k8sModels; }; // URL routes that can be namespaced let namespacedResources; /** * Provides a synchronous way to acquire all statically-defined Kubernetes models. * NOTE: This will not work for CRDs defined at runtime, use `connectToModels` instead. */ export const allModels = getK8sModels; export const getNamespacedResources = () => { if (!namespacedResources) { namespacedResources = new Set(); allModels().forEach((v, k) => { if (!v.namespaced) { return; } if (v.crd) { namespacedResources.add(k); } if (!v.crd || v.legacyPluralURL) { namespacedResources.add(v.plural); } }); } return namespacedResources; };