command line, json, kubernetes, yaml

YAML & JSONPath in kubernetes

YAML vs JSON

Both of these formats can represent the same data in almost the same way. While YAML uses indentation to organize data into list and dictionaries, JSON uses curly and square brackets.

Two examples below represents the same data.

{
  "car" : {
    "color": "white",
    "price": 10000
  }
}
car:
  color: white
  price: 10000

You can play with samples on this site and compare both formats.

YAML syntax

# key-value
Fruit: Apple
Liquid: Water
Meat: Chicken
Vegetable: Potato

# array/list (ordered collection)
Fruits:
  - Orange
  - Apple
  - Banana

# dictionary/map (unordered collection)
Banana:
  Carbs: 27g
  Fat: 2g
  Calories: 105

Operators

There are a few helpful operators in JsonPath:

Root node: $ This symbol denotes the root member of a JSON structure (does not matter if it is an object or array).

Current node: @ Represents the node that is being processed, mostly used when iterating using range command.

Wildcard: * Matches all elements within the current scope.

All results of JSONPath queries are encapsulated in an array []

Filters

Let’s assume we have following JSON:

{
  "cars" : [
    {
      "color": "white",
      "price": 30000
    },
    {
      "color": "black",
      "price": 10000
    },
    {
      "color": "red",
      "price": 20000
    },
    {
      "color": "blue",
      "price": 60000
    },
    {
      "color": "green",
      "price": 90000
    },
  ]
}

Our task is to extract price for the red car. We can do it simply by:

$.cars[2].price

But what if the order of those cars changes? Red car will no longer be on the third position in this array. Hence we have to use filtering conditions. No matter on what position red car will be, correct result will be returned from below query. Filter syntax is: ?(<condition>), @ means current item in list.

$.cars[?(@.color=="red")].price

# result
[
  20000
]

Wildcards

Take a look again at example JSON from the previous section. This time we want to extract only prices from all cars. * ensures it will iterate through all the elements and return only one property, in this case price.

$.cars[*].price

# result
[
  30000,
  10000,
  20000
]

Lists

# get the 1st element
$.cars[0].color

# get 1st to 4th elements
$.cars[0:5].color

# get 1st to 5th element and skip every two [start:end:step]
$.cars[0:6:2].color

# get the last item
$.cars[-1:].color

# get the last 3 elements
$.cars[-3:].color

JSONPath in kubernetes

Why JSONPath?

When you are working with production environment in kubernetes you will need to view information about hundreds of nodes and thousands of objects like deployments, pods, replica sets, services, secrets etc. You will use kubectl cli to get this information. But in many cases you will need to filter information and maybe get some more details that is provided by kubectl default output.

Viewing such specific information by going through thousands of these resources would be overwhelming task. Which is why kubectl supports JSONPath option, that makes filtering data across large data sets an easy way.

kubectl under the hood

Every time you run kubectl command, it interacts with kubernetes API. Then, kube-apiserver sends response in JSON format. kubectl converts it into human-readable output and prints it out to the screen. During that process, a lot of information that came in the response is hidden to make an output readable, showing only necessary fields. We can use -o wide option to see more of it, but that is no complete information. There are still a lot more details which is not shown.

How to make use of this feature?

In order to get started with JSONPath in kubectl, follow these steps:

  • identify the kubectl command that will give you the required information, eg kubectl get nodes
  • inspect it’s output in json pretty format kubectl get nodes -o json | python3 -m json.tool
  • form the JSONPath query
  • compose it together: kubectl get nodes -o=jsonpath='{.items[0].spec.containers[0].image}'

Examples

# print all node names
$ kubectl get nodes -o=jsonpath='{.items[*].metadata.name}'

master node01


# get number of CPU cores on each node
$ kubectl get nodes -o=jsonpath='{.items[*].status.capacity.cpu}'

4 4


# combine both of them
$ kubectl get nodes -o=jsonpath='{.items[*].metadata.name}{.items[*].status.capacity.cpu}'

master node01 4 4


# to make it more readable with empty line between separate query results
$ kubectl get nodes -o=jsonpath='{.items[*].metadata.name}{"\n"}{.items[*].status.capacity.cpu}'

master node01
4      4


# to make it even more readable, we can iterate over it and put it in different order
$ kubectl get nodes -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.capacity.cpu}{"\n"}{end}'

master  4
node01  4


# lastly, we can add custom columns to make it look like default kubectl output
$ kubectl get nodes -o=custom-columns=NODE_NAME:.metadata.name,CPU_COUNT:.status.capacity.cpu

NODE_NAME CPU_COUNT
master    4
node01    4

$ kubectl get deploy -o=custom-columns=DEPLOYMENT:.metadata.name,CONTAINER_IMAGE:.spec.template.spec.containers[0].image,READY_REPLICAS:.status.readyReplicas,NAMESPACE:.metadata.namespace --sort-by=.metadata.name

DEPLOYMENT   CONTAINER_IMAGE   READY_REPLICAS   NAMESPACE
deploy1      nginx             1                default
deploy2      nginx:alpine      1                default
deploy3      nginx:1.16        1                default
deploy4      nginx:1.17        1                default
deploy5      nginx:latest      1                default

# list restartCount from container with image redis:alpine
$ kubectl get pod -o=jsonpath='{.status.containerStatuses[?(@.image=='redis:alpine')].restartCount}'

$ k get po -o custom-columns=POD:.metadata.name,QOS_CLASS:.status.qosClass

POD                          QOS_CLASS
my-static-pod-controlplane   Burstable
static-pod-controlplane      Burstable