Tony Stark
Tony Stark

Reputation: 2468

Multiple json_query in Ansible?

I have the following yaml file.

resources:
  - apiVersion: v1
    kind: Deployment
    metadata:
      labels:
        app: test
      name: test-cluster-operator
      namespace: destiny001
    spec:
      selector:
        matchLabels:
          name: test-cluster-operator
          test.io/kind: cluster-operator
      strategy:
        type: Recreate
      template:
        metadata:
          labels:
            name: test-cluster-operator
            test.io/kind: cluster-operator
        spec:
          containers:
          - args:
            - /path/test/bin/cluster_operator_run.sh
            env:
            - name: MY_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            imagePullPolicy: IfNotPresent
            livenessProbe:
              failureThreshold: 3
              httpGet:
                path: /healthy
                port: 8080
                scheme: HTTP
              initialDelaySeconds: 10
              periodSeconds: 30
              successThreshold: 1
              timeoutSeconds: 1
            name: test-cluster-operator
            readinessProbe:
              failureThreshold: 3
              httpGet:
                path: /ready
                port: 8080
                scheme: HTTP
              initialDelaySeconds: 10
              periodSeconds: 30
              successThreshold: 1
              timeoutSeconds: 1
            resources:
              limits:
                cpu: '1'
                memory: 256Mi
              requests:
                cpu: 200m
                memory: 256Mi
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
            volumeMounts:
            - mountPath: /var/data
              name: data-cluster-operator
          dnsPolicy: ClusterFirst
          restartPolicy: Always
          schedulerName: default-scheduler
          securityContext: {}
          serviceAccount: test-cluster-operator
          serviceAccountName: test-cluster-operator
          terminationGracePeriodSeconds: 30
          volumes:
          - name: data-cluster-operator
            persistentVolumeClaim:
              claimName: data-cluster-operator

I am trying to get the the value of env variable called MY_NAMESPACE. This is what I tried in Ansible to get to the env tree path.

- name: "set test fact"
  set_fact:
    myresult: "{{ yaml_file_variable | json_query(\"resources[?metadata.name=='test-cluster-operator'].spec.template.spec\") | json_query(\"containers[?name=='test-cluster-operator'].env\") }}"

- name: "debug"
  debug:
    msg: "{{ myresult }}"

This produces an empty list, however the first json_query works well.

How do I use json_query correctly in this case? Can I achieve this with just one json_query?

EDIT: I seem to be closer to a solution but the result ist a list and not string, which I find annoying.

- name: "set test fact"
  set_fact:
    myresult: "{{ yaml_file_variable | json_query(\"resources[?metadata.name=='test-cluster-operator'].spec.template.spec\") | json_query(\"[].containers[?name=='test-cluster-operator']\") | json_query(\"[].env[?name=='MY_NAMESPACE'].name\") }}"

This prints - - MY_NAMESPACE instead of just MY_NAMESPACE.

Do I have to use first filter every time after json_query? I know for sure that there is only one containers element. I don't understand why json_query returns a list.

This is finally working but no idea whether it's correct way to do it.

- name: "set test fact"
  set_fact:
    myresult: "{{ yaml_file_variable | json_query(\"resources[?metadata.name=='test-cluster-operator'].spec.template.spec\") | first | json_query(\"containers[?name=='test-cluster-operator']\") | first | json_query(\"env[?name=='MY_NAMESPACE'].valueFrom \") | first }}"

Upvotes: 2

Views: 3309

Answers (1)

Setanta
Setanta

Reputation: 996

json_query uses jmespath and jmespath always returns a list. This is why your first example isn't working. The first query returns a list but the second is trying to query a key. You've corrected that in the second with [].

You're also missing the jmespath pipe expression: | which is used pretty much as you might expect - the result of the first query can be piped into a new one. Note that this is separate from ansible filters using the same character.

This query:

resources[?metadata.name=='test-cluster-operator'].spec.template.spec | [].containers[?name=='test-cluster-operator'][].env[].valueFrom

Should give you the following output:

[
  {
    "fieldRef": {
      "apiVersion": "v1",
      "fieldPath": "metadata.namespace"
    }
  }
]

Your task should look like this:

- name: "set test fact"
  set_fact:
    myresult: "{{ yaml_file_variable | json_query(\"resources[?metadata.name=='test-cluster-operator'].spec.template.spec | [].containers[?name=='test-cluster-operator'][].env[].valueFrom\") | first }}"

To answer your other question, yes you'll need to the first filter. As mentioned jmespath will always return a list, so if you just want the value of a key you'll need to pull it out.

Upvotes: 2

Related Questions