Do you even JSONPath?

Do you even JSONPath?

I like the CLI and a black background console when it comes to checking on things and debugging, it makes me feel more confident in what I'm seeing and closer to the basics.

A big chunk of my recent years has been all about Kubernetes. Kubernetes this, Kubernetes that, mostly EKS professionally, here and there playing around GKS and of course, Minikube is my bro on local.

And kubectl is also a close friend of mine, the first tool kinda everyone learns when starting with Kubernetes, but perhaps the easiest and cleanest too.

JSONPath

But do you know kubectl offers JSONPath support natively? yeah, JSONPath as in a JSON query language that enables you to interact with a JSON structured data set.

Using a set of JSONPath expressions, one could query, parse and format a JSON structure easily to get whatever information needed in a readable and clear way.

Enough with the written words

Let's query some Kubernetes thingy(s).

The usual stuff

Once both are installed, let's deploy a sample pod + service + service account (for this example, I will be using the one to rule them all: httpbin).

# create a new namespace
kubectl create ns jsonpath-playground

# get httpbin's YAML (from Istio examples)
curl -s https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml -O

# deploy httpbin resources
kubectl apply -f httpbin.yaml -n jsonpath-playground

Now we have a set of resources created in our local cluster:

  • A pod.

  • A service.

  • A service account.

Check them out with:

$ kubectl get pod,svc,sa -n jsonpath-playground

NAME                          READY   STATUS    RESTARTS   AGE
pod/httpbin-bf5fc5d74-p9h2g   1/1     Running   0          65s

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/httpbin   ClusterIP   10.100.156.45   <none>        8000/TCP   65s

NAME                     SECRETS   AGE
serviceaccount/default   1         83s
serviceaccount/httpbin   1         65s

By default, kubectl outputs its data in plain-text columns, nice and readable, with a limited amount of information that gives an overview of what we are querying. This is indeed intended to give a quick look at the resources, but not much in-depth.

If we wanted to go beyond that, we could use the -o wide flag, making kubectl output a bit more information.

But let's go deeper. We want to see the service account's raw JSON output. Let's use -o json flag now.

kubectl get sa -n jsonpath-playground httpbin -o json

We get something like this:

{
    "apiVersion": "v1",
    "kind": "ServiceAccount",
    "metadata": {
        "annotations": {
            "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"annotations\":{},\"name\":\"httpbin\",\"namespace\":\"jsonpath-playground\"}}\n"
        },
        "creationTimestamp": "2023-05-22T11:08:34Z",
        "name": "httpbin",
        "namespace": "jsonpath-playground",
        "resourceVersion": "859",
        "uid": "uid"
    },
    "secrets": [
        {
            "name": "httpbin-token-uid"
        }
    ]
}

This is a very simple object, which does not have that many attributes. Let's make it a bit richer by adding an AWS IRSA annotation (see my IRSA article if you are curious about it).

$ kubectl annotate sa -n jsonpath-playground httpbin eks.amazonaws.com/role-arn=arn:aws:iam::123456:role/irsa-role

serviceaccount/httpbin annotated

This will add an annotation to the httpbin service account, mapping it to a dummy AWS IAM role. But fear not my fellow reader, it can be any annotation for this example purpose.

Let's check how it looks now, with:

kubectl get sa -n jsonpath-playground httpbin -o json

Getting something like:

{
    "apiVersion": "v1",
    "kind": "ServiceAccount",
    "metadata": {
        "annotations": {
            "eks.amazonaws.com/role-arn": "arn:aws:iam::123456:role/irsa-role",
            "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"annotations\":{},\"name\":\"httpbin\",\"namespace\":\"jsonpath-playground\"}}\n"
        },
        "creationTimestamp": "2023-05-22T11:08:34Z",
        "name": "httpbin",
        "namespace": "jsonpath-playground",
        "resourceVersion": "1501",
        "uid": "uid"
    },
    "secrets": [
        {
            "name": "httpbin-token-uid"
        }
    ]
}

What if we wanted to see just the service account's annotations? Enter JSONPath.

Let's modify the above command using a (JSONPath) query expression:

kubectl get sa -n jsonpath-playground httpbin -o jsonpath='{.metadata.annotations}'

Obtaining:

{"eks.amazonaws.com/role-arn":"arn:aws:iam::123456:role/irsa-role","kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"annotations\":{},\"name\":\"httpbin\",\"namespace\":\"jsonpath-playground\"}}\n"}

Not the prettiest, but still way more condensed than the first version.

Let's get just one of them. Remember you can get into any JSON structure by specifying an index (or a key in this case since annotations attribute is a dictionary).

Here you also need to escape . and / as they are interpreted JSONPath expressions.

$ kubectl get sa -n jsonpath-playground httpbin -o jsonpath='{.metadata.annotations.eks\.amazonaws\.com\/role-arn}'

arn:aws:iam::123456:role/irsa-role

See? Nice and clear. I use this, for example, whenever I want to check if a specific value is set, and matches whatever I need it to be. It's a very quick way to do so.

Also, you could use this in a script or an automated test.

Above and beyond

Let's do something else, like recursive querying and parsing the httpbin pod structure for its tolerations. Given the pod's tolerations attribute is a list, we can traverse it and extract its elements. Then we can also add some external information to make it more readable.

Let's first check the pod name:

$ kubectl get pod -n jsonpath-playground

NAME                      READY   STATUS    RESTARTS   AGE
httpbin-bf5fc5d74-zgvtl   1/1     Running   0          34m

And then use its name for the next query:

$ kubectl get pod -n jsonpath-playground httpbin-bf5fc5d74-zgvtl -o jsonpath='{range .spec.tolerations[*]}{"key name: "}{.key}{"\n"}{end}'

key name: node.kubernetes.io/not-ready
key name: node.kubernetes.io/unreachable

Feeling the power of the CLI on your side already? I am.

We have done three things:

  • Traversed the .spec.tolerations list and extracted only the key named key from all items, with {range .items}{.key_name}{end}.

  • Added the prefix key name: to make it more readable, by adding a {"key name: "} in between query parameters.

  • Printed the output in different lines to make it even more readable, adding {"\n"}.

Cleaning up

Don't forget to delete Minikube's cluster from your local with:

minikube delete

Also online, because it's not always about Kubernetes

And, if you don't want to spin up a local cluster or you are already tired of kubectl and Kubernetes, you can play around with one of the many online debuggers, like this one. There is a sample JSON input provided, but feel free to use the ones above as well.

Keep in mind the syntax is slightly different than the one we saw before.

Conclusion

I really like kubectl. That's pretty obvious at this point.

Though I checked other tools, like k9s, I found myself always coming back to it.

When I discovered it has JSONPath support, at first it was not that usable for me as I was not using kubectl in that depth. But after a while, it became clear that it is a pretty neat tool to use if you like JSON better than YAML.

Kubectl also allows YAML output with -o yaml flag.

Of course, there are way more options than the ones we used in this example overview, and you can combine them to create much simpler or richer outputs.

References


Thank you for stopping by! Do you know other ways to do this? Please let me know in the comments, I always like to learn how to do things differently.

Did you find this article valuable?

Support Mariano González by becoming a sponsor. Any amount is appreciated!