Hi all,

Among the current pending issues, Christopher and I have been scratching
our heads on an issue involving the var() sample fetch function. It
started in issue 1215 but is more generalized:

     https://github.com/haproxy/haproxy/issues/1215

In short, var() was initially internally declared as returning a string
because it was not possible by then to return "any type". As such, users
regularly get trapped thinking that when they're storing an integer there,
then the integer matching method automatically applies. Except that this
is not possible since this is related to the config parser and is decided
at boot time where the variable's type is not known yet.

As such, what is done is that the output being declared as type string,
the string match will automatically apply, and any value will first be
converted to a string. This results in several issues like:

     http-request set-var(txn.foo) int(-1)
     http-request deny if { var(txn.foo) lt 0 }

not working. This is because the string match on the second line will in
fact compare the string representation of the variable against strings
"lt" and "0", none of which matches.

I had a look at some prod configs from our internal issue tracker and
could also find a few occurrences of the exact same issue. It's even worse
in fact, I found that the vast majority of tests performed on variables
are integer based, and when they work it's only because of exact match,
or inclusive operators such as "ge 1" which will match value 1 but none
above due to the string conversion. And regarding the exact matches they
can even get wrong sometimes, as setting string "-0" will not match string
"0" for example.

Christopher found that the doc says that the matching method is mandatory,
though that's not the case in the code due to that default string type
being permissive. We thought that at the very least we could emit a
warning when no explicit match is placed, but this happens very deep in
the expression evaluator and making a special case just for "var" can
reveal very complicated (but probably not impossible).

Christopher also found that the set-var() converter already mandates a
matching method, as the following will be rejected:

     ... if { int(12),set-var(txn.truc) 12 }

while this one will work:

     ... if { int(12),set-var(txn.truc) eq 12 }

So the question is (as you see me coming), is it acceptable to fix this in
2.5+ by making var() match the doc, returning the type "any", and mandathing
the matching method, implying that this bogus config which does not work:

     http-request set-var(txn.foo) int(-1)
     http-request deny if { var(txn.foo) lt 0 }

will need to be written like this:

     http-request set-var(txn.foo) int(-1)
     http-request deny if { var(txn.foo) -m int lt 0 }

My lock checks showing that roughly 30% of the configs not using -m are
bogus does not even make me want to continue to support this without
telling users their config will not work. And any config fixed with this
will also be fixed and start to work for earlier versions.

I would love to have the option to emit a warning for older ones but I
really cannot promise anything. I expect that 2.5 users will be experienced
users and that it will not be a big deal to rely on this to fix their
configs once for all. I'd rather not do this during an odd->even switch
however.

I'd like to get some opinions quickly on this as while the error will
just be a one-liner fix, the warnings are way more complicated and will
take some time.

Thanks,
Willy


Reply via email to