Skip to content

Commit 0f4f175

Browse files
committed
Add support for easier YAML loading.
1 parent 61db20e commit 0f4f175

File tree

3 files changed

+253
-1
lines changed

3 files changed

+253
-1
lines changed

util/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<dependency>
2626
<groupId>org.yaml</groupId>
2727
<artifactId>snakeyaml</artifactId>
28-
<version>1.18</version>
28+
<version>1.19</version>
2929
</dependency>
3030
<dependency>
3131
<groupId>commons-codec</groupId>
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util;
14+
15+
import com.google.common.reflect.ClassPath;
16+
import java.io.File;
17+
import java.io.FileReader;
18+
import java.io.IOException;
19+
import java.io.Reader;
20+
import java.io.StringReader;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
import java.util.Set;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
public class Yaml {
28+
private static org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml();
29+
private static Map<String, Class<?>> classes = new HashMap<>();
30+
31+
static final Logger logger = LoggerFactory.getLogger(Yaml.class);
32+
33+
public static String getApiGroupVersion(String name) {
34+
if (name.startsWith("AppsV1")) {
35+
return "apps/v1";
36+
}
37+
if (name.startsWith("AppsV1beta1")) {
38+
return "apps/v1beta1";
39+
}
40+
if (name.startsWith("ExtensionsV1beta1")) {
41+
return "extensions/v1beta1";
42+
}
43+
if (name.startsWith("ExtensionsV1")) {
44+
return "extensions/v1";
45+
}
46+
if (name.startsWith("V1beta1")) {
47+
return "v1beta1";
48+
}
49+
if (name.startsWith("V1beta2")) {
50+
return "v1beta2";
51+
}
52+
if (name.startsWith("V1alpha1")) {
53+
return "v1alpha1";
54+
}
55+
if (name.startsWith("V2beta1")) {
56+
return "v2beta1";
57+
}
58+
if (name.startsWith("V2alpha1")) {
59+
return "v2alpha1";
60+
}
61+
if (name.startsWith("V1")) {
62+
return "v1";
63+
}
64+
return name;
65+
}
66+
67+
private static void initModelMap() throws IOException {
68+
ClassPath cp = ClassPath.from(ClassLoader.getSystemClassLoader());
69+
Set<ClassPath.ClassInfo> allClasses = cp.getTopLevelClasses("io.kubernetes.client.models");
70+
71+
for (ClassPath.ClassInfo clazz : allClasses) {
72+
String groupVersion = getApiGroupVersion(clazz.getSimpleName());
73+
int len = groupVersion.replace("/", "").length();
74+
String name = clazz.getSimpleName().substring(len);
75+
classes.put(groupVersion + "/" + name, clazz.load());
76+
}
77+
}
78+
79+
static {
80+
try {
81+
initModelMap();
82+
} catch (Exception ex) {
83+
logger.error("Unexpected exception while loading classes: " + ex);
84+
}
85+
}
86+
87+
/**
88+
* Add a mapping from API Group/version/kind to a Class to use when calling
89+
* <code>load(...)</code>.
90+
*
91+
* Shouldn't really be needed as most API Group/Version/Kind are loaded dynamically
92+
* at startup.
93+
*/
94+
public static void addModelMap(String apiGroupVersion, String kind, Class<?> clazz) {
95+
modelMap.put(apiVersion + "/" + kind, clazz);
96+
}
97+
98+
/**
99+
* Load an API object from a YAML string representation. Returns a concrete typed
100+
* object (e.g. V1Pod)
101+
*
102+
* @param content The YAML content
103+
* @return An instantiation of the object.
104+
* @throws IOException If an error occurs while reading the YAML.
105+
*/
106+
public static Object load(String content) throws IOException {
107+
return load(new StringReader(content));
108+
}
109+
110+
/**
111+
* Load an API object from a YAML file. Returns a concrete typed
112+
* object (e.g. V1Pod)
113+
*
114+
* @param f The file to load.
115+
* @return An instantiation of the object.
116+
* @throws IOException If an error occurs while reading the YAML.
117+
*/
118+
public static Object load(File f) throws IOException {
119+
return load(new FileReader(f));
120+
}
121+
122+
/**
123+
* Load an API object from a stream of data. Returns a concrete typed
124+
* object (e.g. V1Pod)
125+
*
126+
* @param reader The stream to load.
127+
* @return An instantiation of the object.
128+
* @throws IOException If an error occurs while reading the YAML.
129+
*/
130+
public static Object load(Reader reader) throws IOException {
131+
Map<String, Object> data = yaml.load(reader);
132+
String kind = (String) data.get("kind");
133+
if (kind == null) {
134+
throw new IOException("Missing kind in YAML file!");
135+
}
136+
String apiVersion = (String) data.get("apiVersion");
137+
if (apiVersion == null) {
138+
throw new IOException("Missing apiVersion in YAML file!");
139+
}
140+
141+
Class<?> clazz = (Class<?>) classes.get(apiVersion + "/" + kind);
142+
if (clazz == null) {
143+
throw new IOException(
144+
"Unknown apiVersionKind: "
145+
+ apiVersion
146+
+ "/"
147+
+ kind
148+
+ " known kinds are: "
149+
+ classes.toString());
150+
}
151+
return loadAs(new StringReader(yaml.dump(data)), clazz);
152+
}
153+
154+
/**
155+
* Load an API object from a YAML string representation. Returns a concrete typed
156+
* object using the type specified.
157+
*
158+
* @param content The YAML content
159+
* @param clazz The class of object to return.
160+
* @return An instantiation of the object.
161+
* @throws IOException If an error occurs while reading the YAML.
162+
*/
163+
public static <T> T loadAs(String content, Class<T> clazz) {
164+
return yaml.loadAs(new StringReader(content), clazz);
165+
}
166+
167+
/**
168+
* Load an API object from a YAML file. Returns a concrete typed
169+
* object using the type specified.
170+
*
171+
* @param f The YAML file
172+
* @param clazz The class of object to return.
173+
* @return An instantiation of the object.
174+
* @throws IOException If an error occurs while reading the YAML.
175+
*/
176+
public static <T> T loadAs(File f, Class<T> clazz) throws IOException {
177+
return yaml.loadAs(new FileReader(f), clazz);
178+
}
179+
180+
/**
181+
* Load an API object from a YAML stream. Returns a concrete typed
182+
* object using the type specified.
183+
*
184+
* @param reader The YAML stream
185+
* @param clazz The class of object to return.
186+
* @return An instantiation of the object.
187+
* @throws IOException If an error occurs while reading the YAML.
188+
*/
189+
public static <T> T loadAs(Reader reader, Class<T> clazz) {
190+
return yaml.loadAs(reader, clazz);
191+
}
192+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util;
14+
15+
import static org.junit.Assert.*;
16+
17+
import io.kubernetes.client.models.V1ObjectMeta;
18+
import java.io.StringReader;
19+
import java.lang.reflect.Method;
20+
import org.junit.Test;
21+
22+
public class YamlTest {
23+
@Test
24+
public void testLoad() {
25+
String[] kinds = new String[] {"Pod", "Deployment", "ClusterRole", "APIService", "Scale"};
26+
String[] apiVersions =
27+
new String[] {"v1", "v1beta2", "v1alpha1", "v1beta1", "extensions/v1beta1"};
28+
String[] classNames =
29+
new String[] {
30+
"V1Pod",
31+
"V1beta2Deployment",
32+
"V1alpha1ClusterRole",
33+
"V1beta1APIService",
34+
"ExtensionsV1beta1Scale"
35+
};
36+
for (int i = 0; i < kinds.length; i++) {
37+
String kind = kinds[i];
38+
String className = classNames[i];
39+
try {
40+
String input =
41+
"kind: "
42+
+ kind
43+
+ "\n"
44+
+ "apiVersion: "
45+
+ apiVersions[i]
46+
+ "\n"
47+
+ "metadata:\n"
48+
+ " name: foo";
49+
Object obj = Yaml.load(new StringReader(input));
50+
Method m = obj.getClass().getMethod("getMetadata");
51+
V1ObjectMeta metadata = (V1ObjectMeta) m.invoke(obj);
52+
53+
assertEquals("foo", metadata.getName());
54+
assertEquals(className, obj.getClass().getSimpleName());
55+
} catch (Exception ex) {
56+
assertNull("Unexpected exception: " + ex.toString(), ex);
57+
}
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)