A Java DSL for reading YAML documents and replacing values.
YamlPath is available at the Maven Central Repository. To use it, simply declare the following dependency part of your pom file:
<dependency>
<groupId>io.github.yaml-path</groupId>
<artifactId>yaml-path</artifactId>
<version>${latest version in Maven Central}</version>
</dependency>
Also, YAMLPath is available as a command line interface. For further information about how to install it and use it, please go to this link.
The simplest most straight forward way to use YamlPath is via the static API.
String yaml = "apiVersion: v1\n"
+ "kind: Service\n"
+ "metadata:\n"
+ " name: helm-on-kubernetes-example\n";
String name = YamlPath.from(yaml).readSingle("metadata.name");
Using the YamlPath parser, apart from reading values, we can also overwrite values and then dump the updated resources as string.
For example, given the following yaml file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example
If we write the following code:
String newYamlContent = YamlPath.from(file)
.write("metadata.name", "another-name!")
.dumpAsString();
// Output of newYamlContent is:
// ---
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: another-name!
If we want to map a property that is placed at a very depth level, for example, the containerPort
property:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: example
template:
metadata:
app.kubernetes.io/name: example
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
name: example
ports:
- containerPort: 8080 // we want to map this property!
name: http
protocol: TCP
If we want to map the property containerPort
, we would need to write all the parent properties by using the path expression "spec.template.spec.containers.ports.containerPort".
And what about if the containerPort
property is at one place in the Deployment resources, but at another place in the DeploymentConfig resources? We would need to provide two expressions.
To ease up this use case, we can use wildcards. For example, using the expression "*.ports.containerPort", it would map all the container ports at any level.
For example, having the following YAML resource:
apiVersion: v1
kind: Resource
metadata:
name: "a name"
spec:
args:
- value 1
- value 2
- value 3
We can use this expression "spec.args[1]", to return the value at the position 1 which is "value 2".
If you want to select properties which key contains special characters like '.', you need to escape them using '
, for example, using the path expression "".
YamlPath.from(yaml).read("spec.selector.matchLabels.'app.kubernetes.io/name'");
If you want to only map properties of certain resource type, you can add as many conditions you need in the path such as: "(kind == Service).metadata.name".
Additionally, we can write the filter including the "and" operator using "&&" or the "or" operator using "||":
// To map the property only for Service resources AND resources that has an annotation 'key' with value 'some'
YamlPath.from(yaml).read("(kind == Service && metadata.annotations.'key' == 'some.text').metadata.name");
// To map the property only for either Deployment OR DeploymentConfig resources
YamlPath.from(yaml).read("(kind == Deployment || kind == DeploymentConfig).metadata.name");
Also, filters can be placed at any place in the path expression and also at multiple times. Let's see an example of this: we want to map the container port of containers with name example
and only for Deployment resources:
## To map the property only for Deployment resource AND containers with a concrete name
YamlPath.from(yaml).read("(kind == Deployment).spec.template.spec.containers.(name == example).ports.containerPort");
- We can't use regular expressions.
- We can't write complex filters that involves AND/OR conditions. For example: the filter
(kind == Deployment && kind == DeploymentConfig || name == example)
is not supported. - We can't select elements by index. For example, if we want to map the second container, we can't do:
spec.template.spec.containers.2.ports.containerPort
.