1
+ /* ******************************************************************************
2
+ * Copyright (c) 2025 Red Hat, Inc.
3
+ * Distributed under license by Red Hat, Inc. All rights reserved.
4
+ * This program is made available under the terms of the
5
+ * Eclipse Public License v2.0 which accompanies this distribution,
6
+ * and is available at http://www.eclipse.org/legal/epl-v20.html
7
+ *
8
+ * Contributors:
9
+ * Red Hat, Inc. - initial API and implementation
10
+ ******************************************************************************/
11
+ package com.redhat.devtools.intellij.kubernetes.validation
12
+
13
+ import com.intellij.openapi.diagnostic.logger
14
+ import io.fabric8.kubernetes.client.utils.ApiVersionUtil
15
+ import java.io.IOException
16
+ import java.io.InputStream
17
+ import java.util.concurrent.ConcurrentHashMap
18
+
19
+ object KubernetesSchema {
20
+ private val cache = ConcurrentHashMap <String , String >()
21
+
22
+ /* *
23
+ * downloaded from https://github.com/yannh/kubernetes-json-schema/tree/master/v1.31.10-standalone-strict
24
+ */
25
+ private const val SCHEMA_BASE_PATH = " /schemas/k8s.io"
26
+
27
+ fun get (kind : String , apiVersion : String ): String? {
28
+ if (kind.isBlank()
29
+ || apiVersion.isBlank()) {
30
+ logger<KubernetesSchema >().debug(" Invalid parameters: kind='$kind ', apiVersion='$apiVersion '" )
31
+ return null
32
+ }
33
+
34
+ val schemaKey = " $apiVersion /$kind "
35
+ return cache[schemaKey] ? : run {
36
+ val schema = load(kind, apiVersion)
37
+ if (schema != null ) {
38
+ cache[schemaKey] = schema
39
+ }
40
+ schema
41
+ }
42
+ }
43
+
44
+ fun clearCache () {
45
+ cache.clear()
46
+ }
47
+
48
+ private fun load (kind : String , apiVersion : String ): String? {
49
+ // Try different naming patterns to find the schema file
50
+ val possibleFileNames = getPossibleFileNames(kind, apiVersion)
51
+
52
+ return possibleFileNames.asSequence()
53
+ .mapNotNull { fileName ->
54
+ load(fileName)
55
+ }
56
+ .firstOrNull()
57
+ .also { schema ->
58
+ if (schema == null ) {
59
+ logger<KubernetesSchema >().warn(" No schema found for kind: '$kind ' with apiVersion: '$apiVersion '" )
60
+ }
61
+ }
62
+ }
63
+
64
+ private fun load (fileName : String ): String? {
65
+ val resourcePath = " $SCHEMA_BASE_PATH /$fileName "
66
+ logger<KubernetesSchema >().debug(" Trying to load schema from $resourcePath " )
67
+
68
+ return try {
69
+ return loadSchema(resourcePath)
70
+ ? : loadSchema(resourcePath.removePrefix(" /" ))
71
+ } catch (e: IOException ) {
72
+ logger<KubernetesSchema >().debug(" Failed to load schema from $resourcePath " , e)
73
+ null
74
+ }
75
+ }
76
+
77
+ private fun loadSchema (path : String ): String? {
78
+ val inputStream = KubernetesSchema ::class .java.getResourceAsStream(path)
79
+ return inputStream?.use { stream ->
80
+ val schema = stream.readBytes().toString(Charsets .UTF_8 )
81
+ logger<KubernetesSchema >().info(" Successfully loaded schema from $path " )
82
+ schema
83
+ }
84
+ }
85
+
86
+ /* *
87
+ * Returns possible file names for a given kind and apiVersion.
88
+ * This function attempts to generate several possible file names based on the
89
+ * provided `kind` and `apiVersion`. The generated names are ordered by likelihood
90
+ * of a match, starting with the most specific (including group and version) and becoming more general.
91
+ *
92
+ * @param kind The kind of the Kubernetes resource (e.g., "Pod", "Deployment").
93
+ * @param apiVersion The apiVersion of the Kubernetes resource (e.g., "v1", "apps/v1").
94
+ * @return A list of possible schema file names, ordered by specificity.
95
+ */
96
+ internal fun getPossibleFileNames (kind : String , apiVersion : String ): List <String > {
97
+ val kindLower = kind.lowercase()
98
+ val fileNames = mutableListOf<String >()
99
+
100
+ // Parse apiVersion to extract group and version
101
+ val group = ApiVersionUtil .trimGroupOrNull(apiVersion) ? : " "
102
+ val version = ApiVersionUtil .trimVersion(apiVersion)
103
+
104
+ // Pattern 1: kind-group-version.json (e.g., deployment-apps-v1.json)
105
+ if (group.isNotEmpty()) {
106
+ fileNames.add(" $kindLower -$group -$version .json" )
107
+ }
108
+
109
+ // Pattern 2: kind-version.json (e.g., pod-v1.json)
110
+ if (version.isNotEmpty()) {
111
+ fileNames.add(" $kindLower -$version .json" )
112
+ }
113
+
114
+ // Pattern 3: kind.json (e.g., deployment.json)
115
+ fileNames.add(" $kindLower .json" )
116
+ return fileNames
117
+ }
118
+ }
0 commit comments