How to Prune Only Selected Fields in Jsonnet


In Jsonnet’s standard libraries, there’s an std.prune function which will recursively go through an object and remove any empty field in the object which is quite handy, usually. In my case I wanted to prune all empty fields in a container object but spare the legit empty emptyDir field so the std.prune may cause some trouble:

// source
{
  containers: {
    local container = {
      image: 'nginx:latest',
      env: [],
      args: [],
      volumeMounts: [
        {
          name: 'test',
          emptyDir: {}
        }
      ],
    },
    test: std.prune(container),
  },
}
// output
{
  "containers": {
    "test": {
      "image": "nginx:latest", // removed emtpy env and args
      "volumeMounts": [
        {
          "name": "test"
          // the emptyDir should not be removed here
        }
      ]
    }
  }
}

So I wrote my own prune function which only clean up empty field matching a list of names, here’s what it looks like:

// source
{
  containers: {
    local prune(obj) = 
      local field_list = ['args', 'env'];
      {
        [if std.member(field_list, f) && std.length(obj[f]) == 0 then null else f]: obj[f]
        for f in std.objectFields(obj)
      },
    local container = {
      image: 'nginx:latest',
      env: [],
      args: [],
      volumeMounts: [
        {
          name: 'test',
          emptyDir: {}
        }
      ],
    },
    test: prune(container),
  },
}
// output
{
  "containers": {
    "test": {
      "image": "nginx:latest",
      "volumeMounts": [
        {
          "emptyDir": {},
          "name": "test"
        }
      ]
    }
  }
}

This works for the sample but obviously it only goes through the first-level fields. Let’s get recursive!

// source
{
  containers: {
    local pruneExcept(a, ex=[]) =  
      if std.isObject(a) then{
        [if std.member(ex, f) || std.length(a[f]) > 0 then f else null]: $.prune(a[f],ex) 
        for f in std.objectFields(a)
      } else if std.isArray(a) then [
          $.prune(nf, ex) for nf in a
      ] else a,
    local container = {
      image: 'nginx:latest',
      env: [],
      args: [],
      volumeMounts: [
        {
          name: 'test',
          emptyDir: {},
          rubbish: {}
        }
      ],
      resources: {
        requests: {
          cpu: '0.5',
          rubbish: []
        },
        rubbish: [],
      }
    },
    test: pruneExcept(container, 'emptyDir'),
  },
}
// output
{
  "containers": {
    "test": {
      "image": "nginx:latest",
      "resources": {
        "requests": {
          "cpu": "0.5"
        }
      },
      "volumeMounts": [
        {
          "emptyDir": {},
          "name": "test"
        }
      ]
    }
  }

It works but why should I stop here? I had a look at the source code of std.prune and proposed a PR here. 🙂