Skip to content

Commit ca8136c

Browse files
committed
fix #4924: moving multi-doc handling to core logic
1 parent f68cdcb commit ca8136c

File tree

2 files changed

+35
-107
lines changed

2 files changed

+35
-107
lines changed

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
2424
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
2525
import io.fabric8.kubernetes.api.model.KubernetesResource;
26+
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
2627
import io.fabric8.kubernetes.api.model.runtime.RawExtension;
2728
import io.fabric8.kubernetes.client.KubernetesClientException;
2829
import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule;
@@ -39,9 +40,6 @@
3940
import java.util.Collections;
4041
import java.util.List;
4142
import java.util.Map;
42-
import java.util.regex.Matcher;
43-
import java.util.regex.Pattern;
44-
import java.util.stream.Collectors;
4543

4644
public class Serialization {
4745
private Serialization() {
@@ -57,8 +55,6 @@ private Serialization() {
5755

5856
private static volatile ObjectMapper YAML_MAPPER;
5957

60-
private static final String DOCUMENT_DELIMITER = "---";
61-
6258
/**
6359
* {@link ObjectMapper} singleton instance used internally by the Kubernetes client.
6460
*
@@ -213,22 +209,7 @@ public static <T> T unmarshal(InputStream is, ObjectMapper mapper) {
213209
*/
214210
@Deprecated
215211
public static <T> T unmarshal(InputStream is, ObjectMapper mapper, Map<String, String> parameters) {
216-
// it's not well documented which Serialization methods are aware of input that can contain
217-
// multiple docs
218-
String specFile;
219-
try {
220-
specFile = IOHelpers.readFully(is);
221-
} catch (IOException e1) {
222-
throw new RuntimeException("Could not read stream");
223-
}
224-
if (containsMultipleDocuments(specFile)) {
225-
return (T) getKubernetesResourceList(Collections.emptyMap(), specFile);
226-
} else if (specFile.contains(DOCUMENT_DELIMITER)) {
227-
specFile = specFile.replaceAll("^---([ \\t].*?)?\\r?\\n", "");
228-
specFile = specFile.replaceAll("\\n---([ \\t].*?)?\\r?\\n?$", "\n");
229-
}
230-
231-
return unmarshal(new ByteArrayInputStream(specFile.getBytes(StandardCharsets.UTF_8)), mapper, new TypeReference<T>() {
212+
return unmarshal(is, mapper, new TypeReference<T>() {
232213
@Override
233214
public Type getType() {
234215
return KubernetesResource.class;
@@ -247,24 +228,51 @@ private static <T> T unmarshal(InputStream is, ObjectMapper mapper, TypeReferenc
247228
} while (intch > -1 && Character.isWhitespace(intch));
248229
bis.reset();
249230

250-
final T result;
231+
T result = null;
232+
List<KubernetesResource> listResult = null;
251233
if (intch != '{' && intch != '[') {
252234
final Load yaml = new Load(LoadSettings.builder().build());
253-
final Object obj = yaml.loadFromInputStream(bis);
254-
if (obj instanceof Map) {
255-
result = mapper.convertValue(obj, type);
256-
} else {
257-
result = mapper.convertValue(new RawExtension(obj), type);
235+
// if multiple docs exist, only non-null resources will be kept
236+
final Iterable<Object> objs = yaml.loadAllFromInputStream(bis);
237+
for (Object obj : objs) {
238+
Object value = null;
239+
if (obj instanceof Map) {
240+
value = mapper.convertValue(obj, type);
241+
} else if (obj != null) {
242+
value = mapper.convertValue(new RawExtension(obj), type);
243+
}
244+
if (value != null) {
245+
if (result == null) {
246+
result = (T) value;
247+
} else {
248+
if (listResult == null) {
249+
listResult = new ArrayList<>();
250+
accumulateResult(result, listResult);
251+
}
252+
accumulateResult(value, listResult);
253+
}
254+
}
258255
}
259256
} else {
260257
result = mapper.readerFor(type).readValue(bis);
261258
}
259+
if (listResult != null) {
260+
return (T) listResult;
261+
}
262262
return result;
263263
} catch (IOException e) {
264264
throw KubernetesClientException.launderThrowable(e);
265265
}
266266
}
267267

268+
private static <T> void accumulateResult(T result, List<KubernetesResource> listResult) {
269+
if (result instanceof KubernetesResourceList) {
270+
listResult.addAll(((KubernetesResourceList) result).getItems());
271+
} else {
272+
listResult.add((KubernetesResource) result);
273+
}
274+
}
275+
268276
/**
269277
* Unmarshals a {@link String}
270278
* <p>
@@ -382,42 +390,6 @@ public static <T> T unmarshal(InputStream is, TypeReference<T> type, Map<String,
382390
return unmarshal(is, JSON_MAPPER, type, parameters);
383391
}
384392

385-
private static List<KubernetesResource> getKubernetesResourceList(Map<String, String> parameters, String specFile) {
386-
return splitSpecFile(specFile).stream().filter(Serialization::validate)
387-
.map(
388-
document -> (KubernetesResource) Serialization.unmarshal(new ByteArrayInputStream(document.getBytes()), parameters))
389-
.filter(o -> o != null)
390-
.collect(Collectors.toList());
391-
}
392-
393-
static boolean containsMultipleDocuments(String specFile) {
394-
final long validDocumentCount = splitSpecFile(specFile).stream().filter(Serialization::validate)
395-
.count();
396-
return validDocumentCount > 1;
397-
}
398-
399-
private static List<String> splitSpecFile(String aSpecFile) {
400-
final List<String> documents = new ArrayList<>();
401-
final StringBuilder documentBuilder = new StringBuilder();
402-
for (String line : aSpecFile.split("\r?\n")) {
403-
if (line.startsWith(DOCUMENT_DELIMITER)) {
404-
documents.add(documentBuilder.toString());
405-
documentBuilder.setLength(0);
406-
} else {
407-
documentBuilder.append(line).append(System.lineSeparator());
408-
}
409-
}
410-
if (documentBuilder.length() > 0) {
411-
documents.add(documentBuilder.toString());
412-
}
413-
return documents;
414-
}
415-
416-
private static boolean validate(String document) {
417-
Matcher keyValueMatcher = Pattern.compile("(\\S+):\\s(\\S*)(?:\\b(?!:)|$)").matcher(document);
418-
return !document.isEmpty() && keyValueMatcher.find();
419-
}
420-
421393
/**
422394
* Create a copy of the resource via serialization.
423395
*

kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -132,50 +132,6 @@ private String readYamlToString(String path) throws IOException {
132132
.collect(Collectors.joining("\n"));
133133
}
134134

135-
@Test
136-
@DisplayName("containsMultipleDocuments, multiple documents with windows line ends, should return true")
137-
void containsMultipleDocumentsWithMultipleDocumentsAndWindowsLineEnds() {
138-
// Given
139-
final String multiDocument = "---\r\napiVersion: v1\r\nKind: Something\r\n\r\n---\r\napiVersion: v2\r\nKind: Other";
140-
// When
141-
final boolean result = Serialization.containsMultipleDocuments(multiDocument);
142-
// Then
143-
assertThat(result).isTrue();
144-
}
145-
146-
@Test
147-
@DisplayName("containsMultipleDocuments, single document with windows line ends, should return false")
148-
void containsMultipleDocumentsWithSingleDocumentAndWindowsLineEnds() {
149-
// Given
150-
final String multiDocument = "---\r\napiVersion: v1\r\nKind: Something\r\n\r\n";
151-
// When
152-
final boolean result = Serialization.containsMultipleDocuments(multiDocument);
153-
// Then
154-
assertThat(result).isFalse();
155-
}
156-
157-
@Test
158-
@DisplayName("containsMultipleDocuments, multiple documents with linux line ends, should return true")
159-
void containsMultipleDocumentsWithMultipleDocumentsAndLinuxLineEnds() {
160-
// Given
161-
final String multiDocument = "---\napiVersion: v1\nKind: Something\n\n---\napiVersion: v2\nKind: Other";
162-
// When
163-
final boolean result = Serialization.containsMultipleDocuments(multiDocument);
164-
// Then
165-
assertThat(result).isTrue();
166-
}
167-
168-
@Test
169-
@DisplayName("containsMultipleDocuments, single document with linux line ends, should return false")
170-
void containsMultipleDocumentsWithSingleDocumentAndLinuxLineEnds() {
171-
// Given
172-
final String multiDocument = "---\napiVersion: v1\nKind: Something\n\n";
173-
// When
174-
final boolean result = Serialization.containsMultipleDocuments(multiDocument);
175-
// Then
176-
assertThat(result).isFalse();
177-
}
178-
179135
@Test
180136
void testSerializeYamlWithAlias() {
181137
// Given

0 commit comments

Comments
 (0)