forked from redhat-developer/yaml-language-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathk8sSchemaUtil.ts
More file actions
129 lines (113 loc) · 4.33 KB
/
k8sSchemaUtil.ts
File metadata and controls
129 lines (113 loc) · 4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { JSONDocument } from '../parser/jsonDocument';
import { SingleYAMLDocument } from '../parser/yamlParser07';
import { ResolvedSchema } from 'vscode-json-languageservice/lib/umd/services/jsonSchemaService';
import { JSONSchema } from '../jsonSchema';
import { BASE_KUBERNETES_SCHEMA_URL } from '../utils/schemaUrls';
/**
* Attempt to retrieve the schema for a given YAML document based on the Kubernetes GroupVersionKind (GVK).
*
* First, checks for a schema for a matching builtin resource, then it checks for a schema for a CRD.
*
* @param doc the yaml document being validated
* @param kubernetesSchema the resolved copy of the Kubernetes builtin
* @param crdCatalogURI the catalog uri to use to find schemas for custom resource definitions
* @returns a schema uri, or undefined if no specific schema can be identified
*/
export function autoDetectKubernetesSchema(
doc: SingleYAMLDocument | JSONDocument,
kubernetesSchema: ResolvedSchema,
crdCatalogURI: string
): string | undefined {
const gvk = getGroupVersionKindFromDocument(doc);
if (!gvk || !gvk.group || !gvk.version || !gvk.kind) {
return undefined;
}
const builtinResource = autoDetectBuiltinResource(gvk, kubernetesSchema);
if (builtinResource) {
return builtinResource;
}
const customResource = autoDetectCustomResource(gvk, crdCatalogURI);
if (customResource) {
return customResource;
}
return undefined;
}
function autoDetectBuiltinResource(gvk: GroupVersionKind, kubernetesSchema: ResolvedSchema): string | undefined {
const { group, version, kind } = gvk;
const groupWithoutK8sIO = group.replace('.k8s.io', '').replace('rbac.authorization', 'rbac');
const k8sTypeName = `io.k8s.api.${groupWithoutK8sIO.toLowerCase()}.${version.toLowerCase()}.${kind.toLowerCase()}`;
const k8sSchema: JSONSchema = kubernetesSchema.schema;
const matchingBuiltin: string | undefined = (k8sSchema.oneOf || [])
.map((s) => {
if (typeof s === 'boolean') {
return undefined;
}
return s._$ref || s.$ref;
})
.find((ref) => {
if (!ref) {
return false;
}
const lowercaseRef = ref.replace('_definitions.json#/definitions/', '').toLowerCase();
return lowercaseRef === k8sTypeName;
});
if (matchingBuiltin) {
return BASE_KUBERNETES_SCHEMA_URL + matchingBuiltin;
}
return undefined;
}
/**
* Retrieve schema by auto-detecting the Kubernetes GroupVersionKind (GVK) from the document.
* If there is no definition for the GVK in the main kubernetes schema,
* the schema is then retrieved from the CRD catalog.
* Public for testing purpose, not part of the API.
* @param doc
* @param crdCatalogURI The URL of the CRD catalog to retrieve the schema from
*/
export function autoDetectCustomResource(gvk: GroupVersionKind, crdCatalogURI: string): string | undefined {
const { group, version, kind } = gvk;
const groupWithoutK8sIO = group.replace('.k8s.io', '').replace('rbac.authorization', 'rbac');
const k8sTypeName = `io.k8s.api.${groupWithoutK8sIO.toLowerCase()}.${version.toLowerCase()}.${kind.toLowerCase()}`;
if (k8sTypeName.includes('openshift.io')) {
return `${crdCatalogURI}/openshift/v4.15-strict/${kind.toLowerCase()}_${group.toLowerCase()}_${version.toLowerCase()}.json`;
}
const schemaURL = `${crdCatalogURI}/${group.toLowerCase()}/${kind.toLowerCase()}_${version.toLowerCase()}.json`;
return schemaURL;
}
type GroupVersionKind = {
group: string;
version: string;
kind: string;
};
/**
* Retrieve the group, version and kind from the document.
* Public for testing purpose, not part of the API.
* @param doc
*/
export function getGroupVersionKindFromDocument(doc: SingleYAMLDocument | JSONDocument): GroupVersionKind | undefined {
if (doc instanceof SingleYAMLDocument) {
try {
const rootJSON = doc.root.internalNode.toJSON();
if (!rootJSON) {
return undefined;
}
const groupVersion = rootJSON['apiVersion'];
if (!groupVersion) {
return undefined;
}
const [group, version] = groupVersion.split('/');
if (!group || !version) {
return undefined;
}
const kind = rootJSON['kind'];
if (!kind) {
return undefined;
}
return { group, version, kind };
} catch (error) {
console.error('Error parsing YAML document:', error);
return undefined;
}
}
return undefined;
}