Skip to content

Commit

Permalink
Merge pull request #1403 from headlamp-k8s/fix-standard-name-crds
Browse files Browse the repository at this point in the history
Fix standard name CRDs
  • Loading branch information
illume authored Sep 27, 2023
2 parents 1307be6 + 1e0185f commit c3d4cfe
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 29 deletions.
79 changes: 54 additions & 25 deletions frontend/src/lib/k8s/apiProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import helpers, { getHeadlampAPIHeaders, isDebugVerbose } from '../../helpers';
import store from '../../redux/stores/store';
import { getToken, logout, setToken } from '../auth';
import { getCluster } from '../util';
import { ResourceClasses } from '.';
import { KubeMetadata, KubeMetrics, KubeObjectInterface } from './cluster';
import { KubeToken } from './token';

Expand Down Expand Up @@ -521,7 +520,18 @@ function asQuery(queryParams?: QueryParameters): string {
: '';
}

function resourceDefToApiFactory(resourceDef: KubeObjectInterface): ApiFactoryReturn {
async function resourceDefToApiFactory(
resourceDef: KubeObjectInterface
): Promise<ApiFactoryReturn> {
interface APIResourceList {
resources: {
kind: string;
namespaced: boolean;
singularName: string;
name: string;
}[];
[other: string]: any;
}
if (isDebugVerbose('k8s/apiProxy@resourceDefToApiFactory')) {
console.debug('k8s/apiProxy@resourceDefToApiFactory', { resourceDef });
}
Expand All @@ -531,12 +541,7 @@ function resourceDefToApiFactory(resourceDef: KubeObjectInterface): ApiFactoryRe
}

if (!resourceDef.apiVersion) {
throw new Error(`Definition has no apiVersion`);
}

let factoryFunc: typeof apiFactory | typeof apiFactoryWithNamespace = apiFactory;
if (!!resourceDef.metadata?.namespace) {
factoryFunc = apiFactoryWithNamespace;
throw new Error(`Definition ${resourceDef.kind} has no apiVersion`);
}

let [apiGroup, apiVersion] = resourceDef.apiVersion.split('/');
Expand All @@ -553,12 +558,30 @@ function resourceDefToApiFactory(resourceDef: KubeObjectInterface): ApiFactoryRe
throw new Error(`apiVersion has no version string: ${resourceDef.apiVersion}`);
}

// Try to use a known resource class to get the plural from, otherwise fall back to
// generating a plural from the kind (which is very naive).
const knownResource = ResourceClasses[resourceDef.kind];
const resourcePlural = knownResource?.pluralName || resourceDef.kind.toLowerCase() + 's';
// Get details about this resource. We could avoid this for known resources, but
// this way we always get the right plural name and we also avoid eventually getting
// the wrong "known" resource because e.g. there can be CustomResources with the same
// kind as a known resource.
const apiResult: APIResourceList = await request(`/apis/${resourceDef.apiVersion}`, {}, false);
if (!apiResult) {
throw new Error(`Unkown apiVersion: ${resourceDef.apiVersion}`);
}

// Get resource
const resource = apiResult.resources?.find(({ kind }) => kind === resourceDef.kind);

if (!resource) {
throw new Error(`Unkown resource kind: ${resourceDef.kind}`);
}

const hasNamespace = !!resource.namespaced;

return factoryFunc(apiGroup, apiVersion, resourcePlural);
let factoryFunc: typeof apiFactory | typeof apiFactoryWithNamespace = apiFactory;
if (!!hasNamespace) {
factoryFunc = apiFactoryWithNamespace;
}

return factoryFunc(apiGroup, apiVersion, resource.name);
}

function getApiRoot(group: string, version: string) {
Expand Down Expand Up @@ -966,25 +989,31 @@ function combinePath(base: string, path: string) {

export async function apply(body: KubeObjectInterface): Promise<JSON> {
const bodyToApply = _.cloneDeep(body);

let apiEndpoint;
try {
apiEndpoint = await resourceDefToApiFactory(bodyToApply);
} catch (err) {
console.error(`Error getting api endpoint when applying the resource ${bodyToApply}: ${err}`);
throw err;
}

// Check if the default namespace is needed. And we need to do this before
// getting the apiEndpoint because it will affect the endpoint itself.
const isNamespaced = apiEndpoint.isNamespaced;
const { namespace } = body.metadata;
if (!namespace) {
const knownResource = ResourceClasses[body.kind];
if (knownResource?.isNamespaced) {
let defaultNamespace = 'default';

const cluster = getCluster();
if (!!cluster) {
const clusterSettings = helpers.loadClusterSettings(cluster);
defaultNamespace = clusterSettings?.defaultNamespace || defaultNamespace;
}
if (!namespace && isNamespaced) {
let defaultNamespace = 'default';

bodyToApply.metadata.namespace = defaultNamespace;
const cluster = getCluster();
if (!!cluster) {
const clusterSettings = helpers.loadClusterSettings(cluster);
defaultNamespace = clusterSettings?.defaultNamespace || defaultNamespace;
}

bodyToApply.metadata.namespace = defaultNamespace;
}

const apiEndpoint = resourceDefToApiFactory(bodyToApply);
const resourceVersion = bodyToApply.metadata.resourceVersion;

try {
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/lib/k8s/crd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ export function makeCustomResourceClass(
isNamespaced: boolean
) {
// Used for tests
const knownClass = ResourceClasses[args[0][2]];
if (!!knownClass) {
return knownClass;
if (process.env.UNDER_TEST === 'true') {
const knownClass = ResourceClasses[args[0][2]];
if (!!knownClass) {
return knownClass;
}
}

const apiFunc = !!isNamespaced ? apiFactoryWithNamespace : apiFactory;
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import Registry, * as registryToExport from './registry';

window.pluginLib = {
ApiProxy: require('../lib/k8s/apiProxy'),
Crd: require('../lib/k8s/crd'),
ReactMonacoEditor: require('@monaco-editor/react'),
MonacoEditor: require(process.env.NODE_ENV === 'test'
? 'monaco-editor/esm/vs/editor/editor.api.js'
: 'monaco-editor'),
K8s: require('../lib/k8s'),
// Anything that is part of the lib/k8s/ folder should be imported after the K8s import, to
// avoid circular dependencies' issues.
Crd: require('../lib/k8s/crd'),
CommonComponents: require('../components/common'),
MuiCore: require('@material-ui/core'),
MuiStyles: require('@material-ui/styles'),
Expand Down

0 comments on commit c3d4cfe

Please sign in to comment.