Do you have any tips on reversing a json dictionary?

I have a map of IPv4 addresses to environments and I would like to have a map of environments to IPv4s.

1
2
3
4
5
6
7
  {
      "192.0.2.131"    : "TEST-NET-1",
      "198.51.100.151" : "TEST-NET-2",
      "198.51.100.146" : "TEST-NET-2",
      "203.0.113.146"  : "TEST-NET-3",
      "203.0.113.21"   : "TEST-NET-3"
  }

Would become:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "TEST-NET-1": [
    "192.0.2.131"
  ],
  "TEST-NET-2": [
    "198.51.100.146",
    "198.51.100.151"
  ],
  "TEST-NET-3": [
    "203.0.113.146",
    "203.0.113.21"
  ]
}

I think that is too complex to do naively in policy. jq is really good for working with JSON and I found an answer of how to swap key and value of an object using jq stackoverflow.

We can use mapdata() to do that same conversion from within policy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  bundle agent example_mapdata_jq_reverse_key_values
  {

    vars:

        "policy_hub_zone_data" data => '{
        "192.0.2.131"    : "TEST-NET-1",
        "198.51.100.151" : "TEST-NET-2",
        "198.51.100.146" : "TEST-NET-2",
        "203.0.113.146"  : "TEST-NET-3",
        "203.0.113.21"   : "TEST-NET-3"
    }';

        "jq" string => "/usr/bin/jq 'to_entries \
                       | map( {(.value) : {(.key):null} }) \
                       | reduce .[] as $item ({}; . * $item) \
                       | to_entries \
                       | map({key:.key, value:(.value|keys)}) \
                       | from_entries '";

        "rev"
          data => mapdata( "json_pipe",
                           '$(jq)',
                           policy_hub_zone_data );

      # Since mapdata returns an array, you need to pick out the first element
        "picked" data => mergedata( "rev[0]" );

    reports:

        "$(with)" with => string_mustache( "{{ %-top- }}", "picked");
  }

  bundle agent __main__
  {
    methods:
        "example_mapdata_jq_reverse_key_values";
  }

Running that policy produces the reversed map:

R: {
  "TEST-NET-1": [
    "192.0.2.131"
  ],
  "TEST-NET-2": [
    "198.51.100.146",
    "198.51.100.151"
  ],
  "TEST-NET-3": [
    "203.0.113.146",
    "203.0.113.21"
  ]
}