open-policy-agentrego

Rego write a test for every entry with a condition has to have another condition


Example data:

{
    "serviceA_primary": {
        "foo": 1
        "bar": 2
    },
    "serviceA_secondary": {
        "foo": 1,
        "bar": 3
    },
    "serviceB_primary": {
        "bar": 2
    },
    "serviceB_secondary": {
        "bar": 3
    }
    ...
}

I want to write a test that for every key with serviceA in the name, it's bar value matches with the bar value of the corresponding serviceB.

For example, serviceA_primary.bar == serviceB_primary.bar and serviceA_secondary.bar == serviceB_secondary.bar

Honestly don't really know how to approach. I was planning on filtering out serviceB and then for every serivceA do something. But I don't think it's the right approach.

deny_due_to_mismatch[res] if {
    some key, val in input
    res := {k: v |
        regex.match(`serviceA`, key)
        k := mpkhs_coord_name
        v := mpkhs_coord_info
    }
}

Solution

  • TLDR; This logic is simple to express using the every keyword (which you can enable with import rego.v1). For example:

    every_serviceA_is_valid if {
        every name, value in service_a {
            value.bar == service_b[name].bar
        }
    }
    

    To make this happen, I defined two helper rules service_a and service_b that generate maps containing the respective services, keyed by the suffixes (e.g., "primary", "secondary"`, etc.)

    service_a[x] := value if {
        some name, value in input
        startswith(name, "serviceA_")
        x := substring(name, count("serviceA_"), -1)
    }
    
    service_b[x] := value if {
        some name, value in input
        startswith(name, "serviceB_")
        x := substring(name, count("serviceB_"), -1)
    }
    

    There is duplicated logic here, however, if there will only ever be serviceA and serviceB then I think this is fine. On the other hand, if you want to generalize the logic you could write a function that returns a map of services for a given name/prefix. For example:

    services(name) := {key: value |
        some k, value in input
        prefix := sprintf("%v_", [name])
        startswith(k, prefix)
        key := substring(k, count(prefix), -1)
    }
    

    Then you could use the function like so:

    every_serviceA_is_valid_v2 if {
        every name, value in services("serviceA") {
            value.bar == services("serviceB")[name].bar
        }
    }
    

    Here's a full example in the playground: https://play.openpolicyagent.org/p/53Hh2wU2Uh