rabbitmq / discussions

Please use RabbitMQ mailing list for questions. Issues that are questions, discussions or lack details necessary to investigate them are moved to this repository.
3 stars 4 forks source link

Unable to set "undefined" argument value for headers exchange binding #161

Closed shark300 closed 4 years ago

shark300 commented 4 years ago
curl --location --request POST 'https://rabbitmq-test.some.domain/api/bindings/sample-vhost/e/sample.ex/q/sample.q/' \
--header 'Authorization: Basic c2FtcGxlLW1hbjpwYXNzd29yZA==' \
--header 'Content-Type: application/json' \
--data-raw '{"routing_key":"", "arguments":{"type": null}}'`
@Bean
public Binding binding(
    HeadersExchange exchange, Queue queue) {
  return BindingBuilder.bind(queue)
      .to(exchange)
      .where("type")
      .exists();
}

When a binding is created by Spring, arguments could be undefined, but it is not possible to configure on UI or by REST.

lukebakken commented 4 years ago

Are you certain that this code ...

      .where("type")
      .exists();

...is correctly represented by this JSON?

"arguments":{"type": null}}
lukebakken commented 4 years ago

Could you please run your Spring code to create the exchange and binding, then export your RabbitMQ definitions to see what JSON is produce (attach it to your response)? Or, provide a complete set of code I can clone, compile and run to see how Spring does it. That would speed up diagnosis greatly as it would save me the time of figuring out how to get the Spring code running.

shark300 commented 4 years ago

Maybe yes, but I can't figure out how to configure it on UI too. If I leave empty, it causes an empty String not "undefined" on UI.

lukebakken commented 4 years ago

@shark300 please see this comment. Thanks.

shark300 commented 4 years ago

Yes, sure.

michaelklishin commented 4 years ago

We only spend time investigating behaviors that can be reproduced with 3.8.9.

michaelklishin commented 4 years ago

OK, this turned out to be a different arguments value (a binding one, not the queue one).

These curl commands work as expected:

curl -u guest:guest --location --request POST http://localhost:15672/api/bindings/%2F/e/he.1/q/cq.1/ --header 'Content-Type: application/json' --data-raw '{"routing_key":"", "arguments":{}}'

curl -u guest:guest --location --request POST http://localhost:15672/api/bindings/%2F/e/he.1/q/cq.1/ --header 'Content-Type: application/json' --data-raw '{"routing_key":"", "arguments":{"type": "blue"}}'

This does not create a binding:

curl -u guest:guest --location --request POST http://localhost:15672/api/bindings/%2F/e/he.1/q/cq.1/ --header 'Content-Type: application/json' --data-raw '{"routing_key":"", "arguments":{"type": null}}'

and the node logs an exception, {error,null_not_allowed}.

There is no way to tell a headers exchange match on an absence of a value, the closest option is to match on "one of the values" using the "x-match" => "any" argument (as opposed to "x-match" => "all"). So this limitation makes sense to me. The plugin could report validation failure better, e.g. with a 400 response.

shark300 commented 4 years ago

@michaelklishin @lukebakken

I'm trying to figure out Spring and RabbitMQ internal operation, and as I see, Spring works great. If I configure headers exchange with exists() method, it will create (somehow) a working binding and I can't do that with UI.

      .where("type")
      .exists();

match: exists_by_spring no match: exists_by_spring_no_matching

Unfortunately, I can not configure on UI:

empty string on UI: empty_string_by_ui "undefined" string on UI: undefined_as_string_by_ui

Last but not least, I configured Spring like this:

        .where("type")
        .matches("undefined")

I've got a same result as I expected.

undefined_as_string_by_spring

I've tried to export working configuration by this command: rabbitmqctl export_definitions C:\Temp\definitions.file.json (I have configured a local server RabbitMQ 3.8.9, Erlang 23.1.2) but there is no argument have been exported (or imported as well):

definitions.file.json.zip

So to recap, I really don't know how, but Spring can configure "existing" operation on headers exchange, and it works. Management UI can't reproduce that, and I can't export this configurations too.

shark300 commented 4 years ago

I've created a quick sample project too.

https://github.com/shark300/rabbitmq_161

michaelklishin commented 4 years ago

A traffic capture would tell you exactly what your Spring code does. Are you sure that it uses the HTTP API to set up the bindings, for example? In any case, matching on null will not achieve what you likely want. Headers exchanges cannot match on absence of a key.

michaelklishin commented 4 years ago

If there is no argument in the definitions, it means that it's skipped by this Spring code. That would explain why it does not fail validation. The solution to replicate this via the HTTP API would be the same: skip the null value. I have demonstrated above using curl that it works as expected.

shark300 commented 4 years ago

@michaelklishin Good catch, Spring is using AMQP to declare exchanges/queues/bindings; even so, it should be configurable on RabbitMQ UI without Spring Admin. Thank you for your tips. I've captured the traffic by Wireshark.

image

rabbitmq_capture.zip

michaelklishin commented 4 years ago

What exactly should be configurable in the management UI? There is a way to configure arguments but null values are explicitly unsupported and I explained why. Any Web UI form, at least in the current interface, has to worry about blank fields that were never touched by the user, especially because of the declaration property equivalence requirement.

I am not convinced that there is any reason for us to remove the validation; we won't do it because of a single reported case: much more demand for this change should emerge first.

shark300 commented 4 years ago

@michaelklishin I see your point, but unfortunately, I can't configure RabbitMQ exchanges on our staging environments via AMQP (for security reason). I have to open a support ticket for configuring our RabbitMQ bindings but our support teams can't do that on UI or via import_definitions. Due to this there is no way for us to configure headers exchange argument "exists". As I see, although there is a way to configure it headers arguments via AMQP, but it should be configurable on UI too.

shark300 commented 3 years ago

Hi @michaelklishin Do you have any news about this issue? If not, we will develop a migration tool for our support team. This tool can use native AMQP to modify headers's binding instead of calling REST API or importing JSON.