Skip to content

Commit b8fca8b

Browse files
Merge pull request #256 from kondapally1989/master
add Yaml.loadAll() method
2 parents d55c91f + 031fa64 commit b8fca8b

File tree

3 files changed

+226
-76
lines changed

3 files changed

+226
-76
lines changed

util/src/main/java/io/kubernetes/client/util/Yaml.java

Lines changed: 99 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
import org.yaml.snakeyaml.nodes.ScalarNode;
3434

3535
public class Yaml {
36-
private static org.yaml.snakeyaml.Yaml yaml =
37-
new org.yaml.snakeyaml.Yaml(new CustomConstructor());
3836
private static Map<String, Class<?>> classes = new HashMap<>();
3937
private static Map<String, String> apiGroups = new HashMap<>();
4038
private static List<String> apiVersions = new ArrayList<>();
@@ -167,33 +165,8 @@ public static Object load(File f) throws IOException {
167165
* @throws IOException If an error occurs while reading the YAML.
168166
*/
169167
public static Object load(Reader reader) throws IOException {
170-
Map<String, Object> data = yaml.load(reader);
171-
String kind = (String) data.get("kind");
172-
if (kind == null) {
173-
throw new IOException("Missing kind in YAML file!");
174-
}
175-
String apiVersion = (String) data.get("apiVersion");
176-
if (apiVersion == null) {
177-
throw new IOException("Missing apiVersion in YAML file!");
178-
}
179-
180-
Class<?> clazz = (Class<?>) classes.get(apiVersion + "/" + kind);
181-
if (clazz == null) {
182-
// Attempt to detect class from version and kind alone
183-
if (apiVersion.contains("/")) {
184-
clazz = (Class<?>) classes.get(apiVersion.split("/")[1] + "/" + kind);
185-
}
186-
}
187-
if (clazz == null) {
188-
throw new IOException(
189-
"Unknown apiVersionKind: "
190-
+ apiVersion
191-
+ "/"
192-
+ kind
193-
+ " known kinds are: "
194-
+ classes.toString());
195-
}
196-
return loadAs(new StringReader(yaml.dump(data)), clazz);
168+
Map<String, Object> data = getSnakeYaml().load(reader);
169+
return modelMapper(data);
197170
}
198171

199172
/**
@@ -206,7 +179,7 @@ public static Object load(Reader reader) throws IOException {
206179
* @throws IOException If an error occurs while reading the YAML.
207180
*/
208181
public static <T> T loadAs(String content, Class<T> clazz) {
209-
return yaml.loadAs(new StringReader(content), clazz);
182+
return getSnakeYaml().loadAs(new StringReader(content), clazz);
210183
}
211184

212185
/**
@@ -218,7 +191,7 @@ public static <T> T loadAs(String content, Class<T> clazz) {
218191
* @throws IOException If an error occurs while reading the YAML.
219192
*/
220193
public static <T> T loadAs(File f, Class<T> clazz) throws IOException {
221-
return yaml.loadAs(new FileReader(f), clazz);
194+
return getSnakeYaml().loadAs(new FileReader(f), clazz);
222195
}
223196

224197
/**
@@ -231,7 +204,62 @@ public static <T> T loadAs(File f, Class<T> clazz) throws IOException {
231204
* @throws IOException If an error occurs while reading the YAML.
232205
*/
233206
public static <T> T loadAs(Reader reader, Class<T> clazz) {
234-
return yaml.loadAs(reader, clazz);
207+
return getSnakeYaml().loadAs(reader, clazz);
208+
}
209+
210+
/**
211+
* Load list of instantiated API objects from a YAML string representation. Returns list of
212+
* concrete typed objects (e.g. { V1Pod, V1SERVICE })
213+
*
214+
* <p>Order of API objects in list will be preserved according to order of objects in YAML string.
215+
*
216+
* @param content The YAML content
217+
* @return List of instantiated objects.
218+
* @throws IOException If an error occurs while reading the YAML.
219+
*/
220+
public static List<Object> loadAll(String content) throws IOException {
221+
return loadAll(new StringReader(content));
222+
}
223+
224+
/**
225+
* Load list of instantiated API objects from a YAML file. Returns list of concrete typed objects
226+
* (e.g. { V1Pod, V1SERVICE })
227+
*
228+
* <p>Order of API objects in list will be preserved according to order of objects in YAML file.
229+
*
230+
* @param f The file to load.
231+
* @return List of instantiated of the objects.
232+
* @throws IOException If an error occurs while reading the YAML.
233+
*/
234+
public static List<Object> loadAll(File f) throws IOException {
235+
return loadAll(new FileReader(f));
236+
}
237+
238+
/**
239+
* Load list of instantiated API objects from a stream of data. Returns list of concrete typed
240+
* objects (e.g. { V1Pod, V1SERVICE })
241+
*
242+
* <p>Order of API objects in list will be preserved according to order of objects in stream of
243+
* data.
244+
*
245+
* @param reader The stream to load.
246+
* @return List of instantiated of the objects.
247+
* @throws IOException If an error occurs while reading the YAML.
248+
*/
249+
public static List<Object> loadAll(Reader reader) throws IOException {
250+
Iterable<Object> iterable = getSnakeYaml().loadAll(reader);
251+
List<Object> list = new ArrayList<Object>();
252+
for (Object object : iterable) {
253+
if (object != null) {
254+
try {
255+
list.add(modelMapper((Map<String, Object>) object));
256+
} catch (ClassCastException ex) {
257+
logger.error("Unexpected exception while casting: " + ex);
258+
}
259+
}
260+
}
261+
262+
return list;
235263
}
236264

237265
/** Defines constructor logic for custom types in this library. */
@@ -252,4 +280,43 @@ private IntOrString constructIntOrString(ScalarNode node) {
252280
}
253281
}
254282
}
283+
284+
/** @return An instantiated SnakeYaml Object. */
285+
public static org.yaml.snakeyaml.Yaml getSnakeYaml() {
286+
return new org.yaml.snakeyaml.Yaml(new CustomConstructor());
287+
}
288+
289+
/**
290+
* @param data Map that will be converted to concrete API object.
291+
* @return An instantiation of the object.
292+
* @throws IOException
293+
*/
294+
private static Object modelMapper(Map<String, Object> data) throws IOException {
295+
String kind = (String) data.get("kind");
296+
if (kind == null) {
297+
throw new IOException("Missing kind in YAML file!");
298+
}
299+
String apiVersion = (String) data.get("apiVersion");
300+
if (apiVersion == null) {
301+
throw new IOException("Missing apiVersion in YAML file!");
302+
}
303+
304+
Class<?> clazz = (Class<?>) classes.get(apiVersion + "/" + kind);
305+
if (clazz == null) {
306+
// Attempt to detect class from version and kind alone
307+
if (apiVersion.contains("/")) {
308+
clazz = (Class<?>) classes.get(apiVersion.split("/")[1] + "/" + kind);
309+
}
310+
}
311+
if (clazz == null) {
312+
throw new IOException(
313+
"Unknown apiVersionKind: "
314+
+ apiVersion
315+
+ "/"
316+
+ kind
317+
+ " known kinds are: "
318+
+ classes.toString());
319+
}
320+
return loadAs(new StringReader(getSnakeYaml().dump(data)), clazz);
321+
}
255322
}

util/src/test/java/io/kubernetes/client/util/YamlTest.java

Lines changed: 92 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,62 +14,89 @@
1414

1515
import static org.junit.Assert.*;
1616

17+
import com.google.common.io.Resources;
18+
import io.kubernetes.client.models.AppsV1beta1Deployment;
1719
import io.kubernetes.client.models.V1ObjectMeta;
20+
import io.kubernetes.client.models.V1Service;
1821
import io.kubernetes.client.models.V1ServicePort;
22+
import java.io.File;
23+
import java.io.IOException;
1924
import java.io.StringReader;
2025
import java.lang.reflect.Method;
26+
import java.util.List;
2127
import org.junit.Test;
2228

2329
public class YamlTest {
30+
private static final String TEST_YAML_FILE_PATH = Resources.getResource("test.yaml").getPath();
31+
private static final String[] kinds =
32+
new String[] {
33+
"Pod",
34+
"CronJob",
35+
"HorizontalPodAutoscaler",
36+
"ClusterRole",
37+
"Deployment",
38+
"APIService",
39+
"Scale",
40+
"Deployment"
41+
};
42+
private static final String[] apiVersions =
43+
new String[] {
44+
"v1",
45+
"batch/v2alpha1",
46+
"autoscaling/v2beta1",
47+
"rbac.authorization.k8s.io/v1alpha1",
48+
"apps/v1beta2",
49+
"apiregistration.k8s.io/v1beta1",
50+
"extensions/v1beta1",
51+
"apps/v1beta1"
52+
};
53+
private static final String[] classNames =
54+
new String[] {
55+
"V1Pod",
56+
"V2alpha1CronJob",
57+
"V2beta1HorizontalPodAutoscaler",
58+
"V1alpha1ClusterRole",
59+
"V1beta2Deployment",
60+
"V1beta1APIService",
61+
"ExtensionsV1beta1Scale",
62+
"AppsV1beta1Deployment"
63+
};
64+
private static final String input =
65+
"kind: " + "XXXX" + "\n" + "apiVersion: " + "YYYY" + "\n" + "metadata:\n" + " name: foo";
66+
2467
@Test
2568
public void testLoad() {
26-
String[] kinds =
27-
new String[] {
28-
"Pod",
29-
"CronJob",
30-
"HorizontalPodAutoscaler",
31-
"ClusterRole",
32-
"Deployment",
33-
"APIService",
34-
"Scale",
35-
"Deployment"
36-
};
37-
String[] apiVersions =
38-
new String[] {
39-
"v1",
40-
"batch/v2alpha1",
41-
"autoscaling/v2beta1",
42-
"rbac.authorization.k8s.io/v1alpha1",
43-
"apps/v1beta2",
44-
"apiregistration.k8s.io/v1beta1",
45-
"extensions/v1beta1",
46-
"apps/v1beta1"
47-
};
48-
String[] classNames =
49-
new String[] {
50-
"V1Pod",
51-
"V2alpha1CronJob",
52-
"V2beta1HorizontalPodAutoscaler",
53-
"V1alpha1ClusterRole",
54-
"V1beta2Deployment",
55-
"V1beta1APIService",
56-
"ExtensionsV1beta1Scale",
57-
"AppsV1beta1Deployment"
58-
};
5969
for (int i = 0; i < kinds.length; i++) {
60-
String kind = kinds[i];
6170
String className = classNames[i];
6271
try {
63-
String input =
64-
"kind: "
65-
+ kind
66-
+ "\n"
67-
+ "apiVersion: "
68-
+ apiVersions[i]
69-
+ "\n"
70-
+ "metadata:\n"
71-
+ " name: foo";
72-
Object obj = Yaml.load(new StringReader(input));
72+
Object obj =
73+
Yaml.load(
74+
new StringReader(input.replace("XXXX", kinds[i]).replace("YYYY", apiVersions[i])));
75+
Method m = obj.getClass().getMethod("getMetadata");
76+
V1ObjectMeta metadata = (V1ObjectMeta) m.invoke(obj);
77+
78+
assertEquals("foo", metadata.getName());
79+
assertEquals(className, obj.getClass().getSimpleName());
80+
} catch (Exception ex) {
81+
assertNull("Unexpected exception: " + ex.toString(), ex);
82+
}
83+
}
84+
}
85+
86+
@Test
87+
public void testLoadAll() throws IOException {
88+
StringBuilder sb = new StringBuilder();
89+
for (int i = 0; i < kinds.length; i++) {
90+
sb.append(input.replace("XXXX", kinds[i]).replace("YYYY", apiVersions[i]));
91+
sb.append("\n---\n");
92+
}
93+
94+
List<Object> list = null;
95+
list = (List<Object>) Yaml.loadAll(sb.toString());
96+
for (int i = 0; i < kinds.length; i++) {
97+
String className = classNames[i];
98+
try {
99+
Object obj = list.get(i);
73100
Method m = obj.getClass().getMethod("getMetadata");
74101
V1ObjectMeta metadata = (V1ObjectMeta) m.invoke(obj);
75102

@@ -81,6 +108,27 @@ public void testLoad() {
81108
}
82109
}
83110

111+
@Test
112+
public void testLoadAllFile() throws Exception {
113+
List<Object> list = Yaml.loadAll(new File(TEST_YAML_FILE_PATH));
114+
for (Object object : list) {
115+
String type = object.getClass().getSimpleName();
116+
if (type.equals("V1Service")) {
117+
V1Service svc = (V1Service) object;
118+
assertEquals("v1", svc.getApiVersion());
119+
assertEquals("Service", svc.getKind());
120+
assertEquals("mock", svc.getMetadata().getName());
121+
} else if (type.equals("AppsV1beta1Deployment")) {
122+
AppsV1beta1Deployment deploy = (AppsV1beta1Deployment) object;
123+
assertEquals("apps/v1beta1", deploy.getApiVersion());
124+
assertEquals("Deployment", deploy.getKind());
125+
assertEquals("helloworld", deploy.getMetadata().getName());
126+
} else {
127+
throw new Exception("some thing wrong happened");
128+
}
129+
}
130+
}
131+
84132
@Test
85133
public void testLoadIntOrString() {
86134
try {

util/src/test/resources/test.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: mock
5+
labels:
6+
app: mock
7+
spec:
8+
ports:
9+
- port: 99
10+
protocol: TCP
11+
targetPort: 9949
12+
selector:
13+
app: mock
14+
---
15+
apiVersion: apps/v1beta1
16+
kind: Deployment
17+
metadata:
18+
labels:
19+
app: helloworld
20+
name: helloworld
21+
spec:
22+
replicas: 1
23+
template:
24+
metadata:
25+
labels:
26+
app: helloworld
27+
name: helloworld
28+
spec:
29+
containers:
30+
- name: helloworld
31+
image: gcr.io/hightowerlabs/helloworld:0.0.1
32+
imagePullPolicy: Always
33+
args:
34+
- "-http=127.0.0.1:8080"
35+

0 commit comments

Comments
 (0)