# 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.

![](https://media.giphy.com/media/5SKwQMGTR1umLrKC7N/giphy.gif align="center")

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](https://kubernetes.io/docs/reference/kubectl/jsonpath/)? 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](https://goessner.net/articles/JsonPath/index.html#e2), 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

![](https://media.giphy.com/media/fXwd1rBNMmtJyfYFU9/giphy.gif align="center")

Let's query some Kubernetes thingy(s).

### The usual stuff

* Minikube (check [this article](https://blog.mariano.cloud/helm-declaration-environments-helmfile#heading-installation) on how to install it in your local).
    
* [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/#install-with-homebrew-on-macos).
    

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](https://httpbin.org/)).

```bash
# 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:

```bash
$ 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](https://kubernetes.io/docs/reference/kubectl/#formatting-output), **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.

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

We get something like this:

```json
{
    "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](https://blog.mariano.cloud/irsa-in-eks-a-kubernetes-aws-bridge)).

```bash
$ 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](https://blog.mariano.cloud/irsa-in-eks-a-kubernetes-aws-bridge#heading-create-a-kubernetes-job). But fear not my fellow reader, it can be any annotation for this example purpose.

Let's check how it looks now, with:

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

Getting something like:

```json
{
    "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.

![](https://media.giphy.com/media/6o0jq0G2tfv7W/giphy.gif align="center")

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

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

Obtaining:

```json
{"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.

```bash
$ 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:

```bash
$ 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:

```bash
$ 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"}`.
    

![](https://media.giphy.com/media/DoPnrkasVpMCZIpzD9/giphy.gif align="center")

### Cleaning up

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

```bash
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](https://jsonpath.com/). 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](https://k9scli.io/), 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](https://goessner.net/articles/JsonPath/index.html#e2) than the ones we used in this example overview, and you can combine them to create much simpler or richer outputs.

## References

* [JSONPath support in Kubernetes (kubectl) documentation](https://kubernetes.io/docs/reference/kubectl/jsonpath/).
    
* [Online JSONPath evaluator](https://jsonpath.com/).
    
* [Httpbin](https://github.com/istio/istio/blob/master/samples/httpbin/httpbin.yaml) YAML.
    

---

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.
