<p>I am trying to achieve the following using ansible. I have a kubernetes deployment file that I want to add a dynamic amount of volumes and volumeMounts to.</p>

<p>The approach I am currently taking is putting the parts I want to patch in a variable, then try use the combine pipe to merge the different parts together. However instead of adding the parts together like this:</p>

<pre><code>      volumeMounts:
        - name: nfs-standard
          mountPath: /mnt/standard
          readOnly: true
        - name: nfs-share2
          mountPath: /mnt/share2
          readOnly: true
        - name: nfs-system
          mountPath: /mnt/system
          readOnly: true
</code></pre>

<p>It replaces the previous one so I end up having just one. Furthermore, when I try to combine this with the final yaml, it removes other parts.</p>

<p>This is how I tried solving it in ansible, this part if being looped over X amount of times depending on the amount of nfs shares</p>

<pre><code>    - name: "loop over fs types"
      set_fact:
        patch:
          spec:
            template:
              spec:
                containers:
                  - name: fsmonitor
                    volumeMounts:
                      - name: "nfs-{{ NFS_NAME }}"
                        mountPath: "{{ NFS_MOUNT_PATH }}"
                        readOnly: true
                volumes:
                  - name: "nfs-{{ NFS_NAME }}"
                    nfs:
                      server: "{{ NFS_SHARE_IP[0] }}"
                      path: "{{ NFS_PATH[0] }}"


    - name:
      set_fact:
        deployment: "{{ deployment | combine(patch, recursive=True) }}"
</code></pre>

<p>It's missing the image and such which, when deployed to kubernetes, results in an unprocessable entity</p>

<pre><code> ok: [gitlab-deploy] =&gt; {
     "deployment": {
         "apiVersion": "apps/v1",
         "kind": "Deployment",
         "metadata": {
             "name": "fsmonitor-deploy"
         },
         "spec": {
             "template": {
                 "spec": {
                     "containers": [
                         {
                             "name": "fsmonitor",
                             "volumeMounts": [
                                 {
                                     "mountPath": "/mnt/nfs",
                                     "name": "nfs-system",
                                     "readOnly": true
                                 }
                             ]
                         }
                     ],
                     "volumes": [
                         {
                             "name": "nfs-system",
                             "nfs": {
                                 "path": "/nfs/system",
                                 "server": "XXXXXXXXX"
                             }
                         }
                     ]
                 }
             }
         }
     }
 }
</code></pre>

<p>Here is a part of the full deployment (the container part)</p>

<pre><code>                     [
                         {
                             "image": "XXXXXXXXXXXXXXXXX",
                             "imagePullPolicy": "Always",
                             "livenessProbe": {
                                 "httpGet": {
                                     "path": "/metrics",
                                     "port": XXXX
                                 },
                                 "initialDelaySeconds": 3,
                                 "periodSeconds": 3
                             },
                             "name": "fsmonitor",
                             "ports": [
                                 {
                                     "containerPort": XXXX
                                 }
                             ],
                             "resources": {
                                 "limits": {
                                     "cpu": "700m",
                                     "memory": "700Mi"
                                 },
                                 "requests": {
                                     "cpu": "200m",
                                     "memory": "200Mi"
                                 }
                             },
                             "volumeMounts": null
                         }
                     ],
</code></pre>

<p>I hope someone can help me with what I am trying to do, I come from a software engineering background and wrapping my head around how ansible works is difficult at times.</p>

Pim Schwippert
<p>I would take a different approach - create variables with the nfs details and loop over those in a Jinja template. Jinja templates work well with Kubernetes resources and will allow you to use standard syntax like for loops, if/then statements and so on to create your resource files.</p>

<p>While it's probably possible to do something with variables and filters it's going to be far more complex and difficult to maintain.</p>

<p>Lets say your tasks looks like this:</p>

<pre><code>- name: List of volume mounts
  set_fact:
    volume_details:
    - name: nfs-standard
      mountPath: /mnt/standard
      readOnly: true
      server: server1
    - name: nfs-share2
      mountPath: /mnt/share2
      readOnly: true
      server: server1
    - name: nfs-system
      mountPath: /mnt/system
      readOnly: true
      server: server2

- name: Create deployment file
  template:
    src: template.j2
    dest: "/tmp/template.yml"
    mode: 0755
</code></pre>

<p>Create a file called deployment.j2 in the templates folder:</p>

<pre><code>---
deployment:
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: fsmonitor-deploy
  spec:
    template:
      spec:
        containers:
        - name: fsmonitor
          volumeMounts:
{% for volume in volume_details %}
          - mountPath: "{{ volume.mountPath }}"
            name: "{{ volume.name }}"
            readOnly: "{{ volume.readOnly }}"
{% endfor %}
        volumes:
{% for volume in volume_details %}
        - name: "{{ volume.name }}"
          nfs:
            path: "{{ volume.mountPath }}"
            server: "{{ volume.server }}"
{% endfor %}
</code></pre>

<p>After the run deployment.yml will be created:</p>

<pre><code>---
deployment:
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: fsmonitor-deploy
  spec:
    template:
      spec:
        containers:
        - name: fsmonitor
          volumeMounts:
          - mountPath: "/mnt/standard"
            name: "nfs-standard"
            readOnly: "True"
          - mountPath: "/mnt/share2"
            name: "nfs-share2"
            readOnly: "True"
          - mountPath: "/mnt/system"
            name: "nfs-system"
            readOnly: "True"
        volumes:
        - name: "nfs-standard"
          nfs:
            path: "/mnt/standard"
            server: "server1"
        - name: "nfs-share2"
          nfs:
            path: "/mnt/share2"
            server: "server1"
        - name: "nfs-system"
          nfs:
            path: "/mnt/system"
            server: "server2"
</code></pre>

<p>Take a look at the Jinja documentation, it's quite flexible for this kind of application. </p>

<p><a href="https://jinja.palletsprojects.com/en/2.11.x/" rel="nofollow noreferrer">https://jinja.palletsprojects.com/en/2.11.x/</a></p>
