NarūnasK
NarūnasK

Reputation: 4950

Conditionally creating a massive object, generated by several for loops?

I have some condition, if it is satisfied I've to create a massive object and merge it into the other one (at least that's how I understand jsonnet works in general):

local something_something = true;
local tst = {
  n1: {
    a: "10.172.52.0/22",
    b: "10.172.56.0/22",
  },
  n2: {
    c: "10.172.60.0/22",
  },
};

{
  aws_route: {
    public: {
      route_table_id: '${aws_route_table.public.id}',
      destination_cidr_block: '0.0.0.0/0',
      gateway_id: '${aws_internet_gateway.tst.id}',
    }
  } + (
    if something_something then {
    ['nat_%s' % zone]: {
      route_table_id: '${aws_route_table.nat_%s.id}' % zone,
      destination_cidr_block: '0.0.0.0/0',
      nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
    }
    for zone in std.objectFields(tst.n1)
  } else {})
  + (
    if something_something then {
    ['nat_%s' % zone]: {
      route_table_id: '${aws_route_table.nat_%s.id}' % zone,
      destination_cidr_block: '0.0.0.0/0',
      nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
    }
    for zone in std.objectFields(tst.n2)
  } else {})
}

This yields the desired result:

$ ./jsonnet tst.jsonnet
{
   "aws_route": {
      "nat_a": {
         "destination_cidr_block": "0.0.0.0/0",
         "nat_gateway_id": "${aws_nat_gateway.ngw_a.id}",
         "route_table_id": "${aws_route_table.nat_a.id}"
      },
      "nat_b": {
         "destination_cidr_block": "0.0.0.0/0",
         "nat_gateway_id": "${aws_nat_gateway.ngw_b.id}",
         "route_table_id": "${aws_route_table.nat_b.id}"
      },
      "nat_c": {
         "destination_cidr_block": "0.0.0.0/0",
         "nat_gateway_id": "${aws_nat_gateway.ngw_c.id}",
         "route_table_id": "${aws_route_table.nat_c.id}"
      },
      "public": {
         "destination_cidr_block": "0.0.0.0/0",
         "gateway_id": "${aws_internet_gateway.tst.id}",
         "route_table_id": "${aws_route_table.public.id}"
      }
   }
}

This works, but there's a lot of boilerplate, namely I have to use exactly the same if-then-else over and over again... What I'm dreaming of is to have one if-then-else and be able to set my massive object in one go, something along the lines:

{
  aws_route: {
    public: {
      route_table_id: '${aws_route_table.public.id}',
      destination_cidr_block: '0.0.0.0/0',
      gateway_id: '${aws_internet_gateway.tst.id}',
    }
  } + (
    if something_something then {
    ['nat_%s' % zone]: {
      route_table_id: '${aws_route_table.nat_%s.id}' % zone,
      destination_cidr_block: '0.0.0.0/0',
      nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
    }
    for zone in std.objectFields(tst.n1),
    ['nat_%s' % zone]: {
      route_table_id: '${aws_route_table.nat_%s.id}' % zone,
      destination_cidr_block: '0.0.0.0/0',
      nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
    }
    for zone in std.objectFields(tst.n2)
  } else {})
}

Sadly this is not supported:

STATIC ERROR: tst.jsonnet:26:41: expected for, if or "}" after for clause, got: ","

Does my take make sense in jsonnet in general, are there better ways to achieve the same?

Upvotes: 0

Views: 574

Answers (1)

jjo
jjo

Reputation: 3020

1) fixed if-then-else clause

You can aggregate both comprehensions inside the if-then-else clause, the below code implements it:

local something_something = true;
local tst = {
  n1: {
    a: '10.172.52.0/22',
    b: '10.172.56.0/22',
  },
  n2: {
    c: '10.172.60.0/22',
  },
};
{
  aws_route: {
    public: {
      route_table_id: '${aws_route_table.public.id}',
      destination_cidr_block: '0.0.0.0/0',
      gateway_id: '${aws_internet_gateway.tst.id}',
    },
  } + (
    // Aggregate (merge actually) both comprehensions, i.e. this construct:
    //   if cond then {obj} + {obj} else {}
    // where obj are the below comprehensions
    if something_something then {
      ['nat_%s' % zone]: {
        route_table_id: '${aws_route_table.nat_%s.id}' % zone,
        destination_cidr_block: '0.0.0.0/0',
        nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
      }
      for zone in std.objectFields(tst.n1)
    } + {
      ['nat_%s' % zone]: {
        route_table_id: '${aws_route_table.nat_%s.id}' % zone,
        destination_cidr_block: '0.0.0.0/0',
        nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
      }
      for zone in std.objectFields(tst.n2)
    }
    else {}
  ),
}

2) using function and looping in nested zones object

After the above fix, generalize to loop inside nested zones object

local something_something = true;
local tst = {
  n1: {
    a: '10.172.52.0/22',
    b: '10.172.56.0/22',
  },
  n2: {
    c: '10.172.60.0/22',
  },
};
// Loop over nested zones object to manifest nat blocks
local aws_nat(cond, zones) = (
  if cond then {
    ['nat_%s' % zone]: {
      route_table_id: '${aws_route_table.nat_%s.id}' % zone,
      destination_cidr_block: '0.0.0.0/0',
      nat_gateway_id: '${aws_nat_gateway.ngw_%s.id}' % zone,
    }
    for entry in std.objectFields(zones)
    for zone in std.objectFields(zones[entry])
  } else {}
);
{
  aws_route: {
    public: {
      route_table_id: '${aws_route_table.public.id}',
      destination_cidr_block: '0.0.0.0/0',
      gateway_id: '${aws_internet_gateway.tst.id}',
    },
  } + aws_nat(something_something, tst),
}

Upvotes: 1

Related Questions