Skip to content

Commit 3b53b68

Browse files
Merge pull request #207 from brendandburns/yaml
Add support for easier YAML loading.
2 parents f6ceff2 + b38b50e commit 3b53b68

File tree

3 files changed

+249
-1
lines changed

3 files changed

+249
-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: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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 <code>load(...)</code>
89+
* .
90+
*
91+
* <p>Shouldn't really be needed as most API Group/Version/Kind are loaded dynamically at startup.
92+
*/
93+
public static void addModelMap(String apiGroupVersion, String kind, Class<?> clazz) {
94+
classes.put(apiGroupVersion + "/" + kind, clazz);
95+
}
96+
97+
/**
98+
* Load an API object from a YAML string representation. Returns a concrete typed object (e.g.
99+
* V1Pod)
100+
*
101+
* @param content The YAML content
102+
* @return An instantiation of the object.
103+
* @throws IOException If an error occurs while reading the YAML.
104+
*/
105+
public static Object load(String content) throws IOException {
106+
return load(new StringReader(content));
107+
}
108+
109+
/**
110+
* Load an API object from a YAML file. Returns a concrete typed object (e.g. V1Pod)
111+
*
112+
* @param f The file to load.
113+
* @return An instantiation of the object.
114+
* @throws IOException If an error occurs while reading the YAML.
115+
*/
116+
public static Object load(File f) throws IOException {
117+
return load(new FileReader(f));
118+
}
119+
120+
/**
121+
* Load an API object from a stream of data. Returns a concrete typed object (e.g. V1Pod)
122+
*
123+
* @param reader The stream to load.
124+
* @return An instantiation of the object.
125+
* @throws IOException If an error occurs while reading the YAML.
126+
*/
127+
public static Object load(Reader reader) throws IOException {
128+
Map<String, Object> data = yaml.load(reader);
129+
String kind = (String) data.get("kind");
130+
if (kind == null) {
131+
throw new IOException("Missing kind in YAML file!");
132+
}
133+
String apiVersion = (String) data.get("apiVersion");
134+
if (apiVersion == null) {
135+
throw new IOException("Missing apiVersion in YAML file!");
136+
}
137+
138+
Class<?> clazz = (Class<?>) classes.get(apiVersion + "/" + kind);
139+
if (clazz == null) {
140+
throw new IOException(
141+
"Unknown apiVersionKind: "
142+
+ apiVersion
143+
+ "/"
144+
+ kind
145+
+ " known kinds are: "
146+
+ classes.toString());
147+
}
148+
return loadAs(new StringReader(yaml.dump(data)), clazz);
149+
}
150+
151+
/**
152+
* Load an API object from a YAML string representation. Returns a concrete typed object using the
153+
* type specified.
154+
*
155+
* @param content The YAML content
156+
* @param clazz The class of object to return.
157+
* @return An instantiation of the object.
158+
* @throws IOException If an error occurs while reading the YAML.
159+
*/
160+
public static <T> T loadAs(String content, Class<T> clazz) {
161+
return yaml.loadAs(new StringReader(content), clazz);
162+
}
163+
164+
/**
165+
* Load an API object from a YAML file. Returns a concrete typed object using the type specified.
166+
*
167+
* @param f The YAML file
168+
* @param clazz The class of object to return.
169+
* @return An instantiation of the object.
170+
* @throws IOException If an error occurs while reading the YAML.
171+
*/
172+
public static <T> T loadAs(File f, Class<T> clazz) throws IOException {
173+
return yaml.loadAs(new FileReader(f), clazz);
174+
}
175+
176+
/**
177+
* Load an API object from a YAML stream. Returns a concrete typed object using the type
178+
* specified.
179+
*
180+
* @param reader The YAML stream
181+
* @param clazz The class of object to return.
182+
* @return An instantiation of the object.
183+
* @throws IOException If an error occurs while reading the YAML.
184+
*/
185+
public static <T> T loadAs(Reader reader, Class<T> clazz) {
186+
return yaml.loadAs(reader, clazz);
187+
}
188+
}
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)