Search code examples
jsonnetksonnet

how to replace one element of array in jsonnet with mixin way?


we have array imported from libsonnet

{
  "volumes": [
    {
      "emptyDir": {},
      "name": "grafana-storage"
    },
    {
      "configMap": {
        "name": "grafana-dashboard-apiserver"
      },
      "name": " grafana-dashboard-apiserver"
    }
  ]
}

I want to replace this element:

    {
      "emptyDir": {},
      "name": "grafana-storage"
    },

with this one (same name):

    {
      "name": "grafana-storage",
      "persistentVolumeClaim": {
        "claimName": "product-nfs"
      }

outputs expected :

{
  "volumes": [
    {
      "configMap": {
        "name": "grafana-dashboard-apiserver"
      },
      "name": " grafana-dashboard-apiserver"
    },
    {
      "name": "grafana-storage",
      "persistentVolumeClaim": {
        "claimName": "product-nfs"
      }
    }
  ]
}

"appending" is easy, but how to remove the existing one

example:

local x = {volumes: [
  {emptyDir: {}, name: 'grafana-storage' },
  {configMap: {name:  'grafana-dashboard-apiserver'
}, name: ' grafana-dashboard-apiserver'},]};

x 
+ {volumes+: [{name: 'grafana-storage',persistentVolumeClaim: {claimName:'product-nfs'}}]}

thanks in advance~


Solution

  • When it comes to deriving/overloading, jsonnet is much easier to work with maps (aka "objects", "dicts", "hashes"), the strategy would be: 1) convert to map, 2) override, 3) convert back to array.

    Below snippet implements it:

    local main_obj = {
      volumes: [
        {
          emptyDir: {},
          name: "grafana-storage",
        },
        {
          configMap: {
            name: "grafana-dashboard-apiserver",
          },
          name: "grafana-dashboard-apiserver",
        },
      ],
    };
    
    local obj_overrides = {
      volumes: [
        {
          name: "grafana-storage",
          persistentVolumeClaim: { claimName: "product-nfs" },
        },
      ],
    };
    
    // Convert to map for easier overloading, assumes all array elements are maps having "name" field
    local toNamedMap(array) = { [x.name]: x for x in array };
    
    // Convert back to array
    local toNamedArray(map) = [{ name: x } + map[x] for x in std.objectFields(map)];
    
    toNamedArray(toNamedMap(main_obj.volumes) + toNamedMap(obj_overrides.volumes))