@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
JavaScript
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;
};