diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..db84685
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+target/
+*.iml
+.idea/
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c5212ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+Cleaner for yaml files that openshift has created using oc get [resource] -o yaml.
+NB. Work in progress: it works fine, but it might need adding more remove actions for certain outputs.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c3e9028
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+ nl.sander
+ yaml-cleaner
+ 0.1-SNAPSHOT
+ yaml-cleaner
+
+
+ 2.10.4
+
+
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${jackson.version}
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+
+
+ org.projectlombok
+ lombok
+ 1.18.14
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.6.2
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 11
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/nl/sander/Cleaner.java b/src/main/java/nl/sander/Cleaner.java
new file mode 100644
index 0000000..0d2cd40
--- /dev/null
+++ b/src/main/java/nl/sander/Cleaner.java
@@ -0,0 +1,64 @@
+package nl.sander;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+public class Cleaner {
+ private final String yamlFile;
+ private final ObjectMapper objectMapper;
+
+ public Cleaner(String filename) {
+ this.yamlFile = filename;
+ objectMapper = new ObjectMapper(new YAMLFactory());
+ objectMapper.findAndRegisterModules();
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ System.out.println("usage: nl.sander.Cleaner ");
+ System.exit(-1);
+ }
+
+ String filename = args[0];
+ new Cleaner(filename).run();
+ }
+
+ private void run() {
+ try {
+ Object resource = objectMapper.readValue(new File(yamlFile), Object.class);
+ Map cleaned = clean(YamlObject.of(resource));
+ objectMapper.writeValue(new File(yamlFile + "_cleaned"), cleaned);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private Map clean(YamlObject resource) {
+ resource.get("items")
+ .forEach(item -> {
+ YamlObject metadata = item.get("metadata");
+ metadata.remove("managedFields");
+ metadata.clear("annotations");
+ metadata.remove("creationTimestamp");
+ metadata.get("labels").removeIf("_key!=app");
+ metadata.remove("selfLink");
+ metadata.remove("namespace");
+ metadata.remove("resourceVersion");
+ metadata.remove("uid");
+ YamlObject spec = item.get("spec");
+ spec.clear("template/metadata/annotations");
+ spec.remove("template/spec/containers/0/image");
+ spec.remove("template/metadata/creationTimestamp");
+ spec.remove("triggers/1/imageChangeParams/lastTriggeredImage");
+ spec.removeIf("triggers/1/imageChangeParams/from/namespace != openshift");
+ item.remove("status");
+ });
+
+ return resource.asMap();
+ }
+}
diff --git a/src/main/java/nl/sander/Union.java b/src/main/java/nl/sander/Union.java
new file mode 100644
index 0000000..03e62a2
--- /dev/null
+++ b/src/main/java/nl/sander/Union.java
@@ -0,0 +1,79 @@
+package nl.sander;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+@SuppressWarnings("unchecked")
+public class Union {
+
+ private final List> types = new ArrayList<>();
+ private Object value;
+
+
+ public Union(Class>... types) {
+ if (Arrays.stream(types).distinct().count() != types.length) {
+ throw new IllegalArgumentException("types must be unique");
+ } else {
+ for (Class> left : types) {
+ for (Class> right : types) {
+ if (left != right && (left.isAssignableFrom(right) || right.isAssignableFrom(left))) {
+ throw new IllegalArgumentException("types must not belong to same class hierarchy");
+ }
+ }
+ }
+ this.types.addAll(Arrays.asList(types));
+ }
+ }
+
+
+ public void set(Object newValue) {
+ if (newValue == null) {
+ throw new NullPointerException();
+ }
+ checkType(newValue.getClass());
+ this.value = newValue;
+ }
+
+
+ public T get() {
+ return (T) this.value;
+ }
+
+ public void when(Class type, Consumer consumer) {
+ checkType(type);
+ if (type.isAssignableFrom(value.getClass())) {
+ consumer.accept((T) value);
+ }
+ }
+
+ public Optional map(Class type, Function function) {
+ checkType(type);
+
+ if (type.isAssignableFrom(value.getClass())) {
+ return Optional.ofNullable(function.apply((T) value));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private void checkType(Class> someType) {
+ Class> selectedType = null;
+ for (Class> type : types) {
+ if (type.isAssignableFrom(someType)) {
+ selectedType = type;
+ }
+ }
+ if (selectedType == null) {
+ throw new IllegalArgumentException("unknown type for this union: " + someType.getName());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Union{ value=" + value.getClass() + '}';
+ }
+}
diff --git a/src/main/java/nl/sander/YamlObject.java b/src/main/java/nl/sander/YamlObject.java
new file mode 100644
index 0000000..3edbd5e
--- /dev/null
+++ b/src/main/java/nl/sander/YamlObject.java
@@ -0,0 +1,128 @@
+package nl.sander;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+//this is almost exactly unlike xpath
+public class YamlObject {
+ private final Union union = new Union(Map.class, List.class, String.class);
+
+ public static YamlObject of(Object value) {
+ YamlObject yamlObject = new YamlObject();
+ yamlObject.union.set(value);
+ return yamlObject;
+ }
+
+ public YamlObject get(String path) {
+ YamlObject result = this;
+ for (String element : path.split("/")) {
+ result = result.doGget(element);
+ }
+ return result;
+ }
+
+ private YamlObject doGget(String key) {
+ Optional optionalYamlObject = union.map(List.class, l -> YamlObject.of(l.get(Integer.parseInt(key))));
+ return optionalYamlObject
+ .orElseGet(() -> union.map(Map.class, m -> YamlObject.of(m.get(key)))
+ .orElseThrow(() -> new IllegalArgumentException("Not a collection object: " + union)));
+ }
+
+ public void clear(String path) {
+ get(path).clear();
+ }
+
+ public void clear() {
+ union.when(Map.class, Map::clear);
+ union.when(List.class, List::clear);
+ }
+
+ public void remove(String path) {
+ executeOn(path, (fetchedObject, lastPathElement) -> {
+ fetchedObject.union.when(Map.class, m -> m.remove(lastPathElement));
+ fetchedObject.union.when(List.class, l -> l.remove(Integer.parseInt(lastPathElement)));
+ });
+ }
+
+ private void executeOn(String path, BiConsumer consumer) {
+ if (path.contains("/")) {
+ while (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
+ }
+ int lastSlash = path.lastIndexOf('/');
+ String head = path.substring(0, lastSlash);
+ YamlObject object = get(head);
+
+ String tail = path.substring(lastSlash + 1);
+ consumer.accept(object, tail);
+ } else {
+ consumer.accept(this, path);
+ }
+ }
+
+ public void remove(int index) {
+ union.when(List.class, l -> l.remove(index));
+ }
+
+ public YamlObject retain(String... keys) {
+ List keyList = Arrays.asList(keys);
+ union.when(Map.class, m -> m.keySet().removeIf(key -> !keyList.contains(key)));
+ return this;
+ }
+
+ public Map asMap() {
+ return union.map(Map.class, m -> (Map) m).orElseThrow(() -> new RuntimeException("Not a map"));
+ }
+
+ public void removeIf(String path) {
+ executeOn(path, this::removeIf);
+ }
+
+ private void removeIf(YamlObject fetchedYamlObject, String expression) {
+ boolean opEquals;
+ if (expression.contains("==")) {
+ opEquals = true;
+ } else if (expression.contains("!=")) {
+ opEquals = false;
+ } else {
+ throw new IllegalArgumentException("syntax exception");
+ }
+
+ String[] tokens = expression.split("[!=]=");
+ String key = tokens[0].trim();
+ String operand = tokens[1].trim();
+
+ if (key.equals("_key") || key.equals("_value")) {
+ fetchedYamlObject.union.when(Map.class, m -> {
+ for (Iterator> iterator = m.entrySet().iterator(); iterator.hasNext(); ) {
+ Map.Entry, ?> entry = iterator.next();
+ Object selector;
+ if (key.equals("_key")) {
+ selector = entry.getKey();
+ } else {
+ selector = entry.getValue();
+ }
+ if ((opEquals && selector.equals(operand)) || (!opEquals && !selector.equals(operand))) {
+ iterator.remove();
+ }
+ }
+ });
+ } else {
+ String value = fetchedYamlObject.get(key).toString();
+ boolean remove = (operand.equals(value) && opEquals) || (!operand.equals(value) && !opEquals);
+ if (remove) {
+ fetchedYamlObject.union.when(Map.class, m -> m.remove(key));
+ }
+ }
+ }
+
+ public void forEach(Consumer consumer) {
+ union.when(List.class, list -> list.forEach(item -> consumer.accept(YamlObject.of(item))));
+ }
+
+ @Override
+ public String toString() {
+ return union.map(String.class, m -> m).orElse(super.toString());
+ }
+}
diff --git a/src/test/java/nl/sander/UnionTest.java b/src/test/java/nl/sander/UnionTest.java
new file mode 100644
index 0000000..7159531
--- /dev/null
+++ b/src/test/java/nl/sander/UnionTest.java
@@ -0,0 +1,34 @@
+package nl.sander;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class UnionTest {
+
+ @Test
+ public void testGet() {
+ Union stringIntegerUnion = new Union(String.class, Integer.class, Long.class);
+ stringIntegerUnion.set("2");
+
+ assertEquals("2", stringIntegerUnion.get());
+ }
+
+ @Test
+ public void testGet2() {
+ Union stringIntegerUnion = new Union(String.class, Integer.class, Long.class);
+ stringIntegerUnion.set(1);
+ int value = stringIntegerUnion.get();
+ assertEquals(1, value);
+ }
+
+ @Test
+ public void testMap() {
+ Union union = new Union(String.class, Integer.class);
+ union.set(1);
+ StringBuilder result = new StringBuilder();
+ union.when(Integer.class, v -> result.append("int ").append(v));
+ union.when(String.class, v -> result.append("string ").append(v));
+ assertEquals("int 1", result.toString());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/nl/sander/YamlObjectTest.java b/src/test/java/nl/sander/YamlObjectTest.java
new file mode 100644
index 0000000..210eb20
--- /dev/null
+++ b/src/test/java/nl/sander/YamlObjectTest.java
@@ -0,0 +1,127 @@
+package nl.sander;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class YamlObjectTest {
+
+ @Test
+ public void testSimpleRemove() {
+ Map