Skip to content

Commit

Permalink
avoid endless recursion (#43)
Browse files Browse the repository at this point in the history
avoid endless recursion when cyclic exchange-to-exchange bindings are encountered (fixes #42)
  • Loading branch information
jandelgado authored Apr 10, 2020
1 parent 1fdeb0d commit aa06467
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

# Changelog for rabtap

## v1.23 (2020-04-09)

* fix: avoid endless recursion in info command (#42)

## v1.22 (2020-01-28)

* The `pub` command now allows ialso to replay messages from a direcory previously
Expand Down
61 changes: 39 additions & 22 deletions cmd/rabtap/broker_info_tree_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,31 +216,48 @@ func (s defaultBrokerInfoTreeBuilder) createQueueNodeFromBinding(
func (s defaultBrokerInfoTreeBuilder) createExchangeNode(
exchange rabtap.RabbitExchange, brokerInfo rabtap.BrokerInfo) *exchangeNode {

exchangeNode := exchangeNode{baseNode{[]interface{}{}}, exchange}

// process all bindings for current exchange
for _, binding := range rabtap.FindBindingsForExchange(exchange, brokerInfo.Bindings) {
if binding.DestinationType == "exchange" {
// exchange to exchange binding
i := rabtap.FindExchangeByName(
brokerInfo.Exchanges,
binding.Vhost,
binding.Destination)
if i != -1 {
exchangeNode.Add(
s.createExchangeNode(
brokerInfo.Exchanges[i],
brokerInfo))
} // TODO else log error
} else {
// queue to exchange binding
queues := s.createQueueNodeFromBinding(binding, exchange, brokerInfo)
for _, queue := range queues {
exchangeNode.Add(queue)
// to detect cyclic exchange-to-exchange bindings. Yes, this is possible.
visited := map[string]bool{}

var create func(rabtap.RabbitExchange, rabtap.BrokerInfo) *exchangeNode
create = func(exchange rabtap.RabbitExchange, brokerInfo rabtap.BrokerInfo) *exchangeNode {

exchangeNode := exchangeNode{baseNode{[]interface{}{}}, exchange}

// process all bindings for current exchange
for _, binding := range rabtap.FindBindingsForExchange(exchange, brokerInfo.Bindings) {
if binding.DestinationType == "exchange" {
// exchange to exchange binding
i := rabtap.FindExchangeByName(
brokerInfo.Exchanges,
binding.Vhost,
binding.Destination)
if i == -1 {
// ignore if not found
continue
}
boundExchange := brokerInfo.Exchanges[i]
if _, found := visited[boundExchange.Name]; found {
// cyclic exchange-to-exchange binding detected
continue
}
visited[boundExchange.Name] = true
exchangeNode.Add(create(exchange, brokerInfo))
} else {
// do not add (redundant) queues if in recursive exchange call
if len(visited) > 0 {
continue
}
// queue to exchange binding
queues := s.createQueueNodeFromBinding(binding, exchange, brokerInfo)
for _, queue := range queues {
exchangeNode.Add(queue)
}
}
}
return &exchangeNode
}
return &exchangeNode
return create(exchange, brokerInfo)
}

func (s defaultBrokerInfoTreeBuilder) createRootNode(rootNodeURL string,
Expand Down
7 changes: 6 additions & 1 deletion cmd/rabtap/cmd_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ func Example_cmdInfoByExchangeInTextFormat() {
// │ └── header-q2 (queue, key='headers-q2', idle since 2017-05-25 19:14:47, [D])
// └── test-topic (exchange, type 'topic', [D])
// ├── topic-q1 (queue, key='topic-q1', idle since 2017-05-25 19:14:17, [D|AD|EX])
// └── topic-q2 (queue, key='topic-q2', idle since 2017-05-25 19:14:21, [D])
// ├── topic-q2 (queue, key='topic-q2', idle since 2017-05-25 19:14:21, [D])
// └── test-topic (exchange, type 'topic', [D])

}

Expand Down Expand Up @@ -182,12 +183,16 @@ const expectedResultDotByExchange = `graph broker {
"exchange_test-topic" -- "boundqueue_topic-q1" [fontsize=10; headport=n; label="topic-q1"];
"exchange_test-topic" -- "boundqueue_topic-q2" [fontsize=10; headport=n; label="topic-q2"];
"exchange_test-topic" -- "exchange_test-topic" [fontsize=10; headport=n; label=""];
"boundqueue_topic-q1" [shape="record"; label="{ topic-q1 | { D | AD | EX } }"];
"boundqueue_topic-q2" [shape="record"; label="{ topic-q2 | { D | | } }"];
"exchange_test-topic" [shape="record"; label="{ test-topic |topic | { D | | } }"];
}`

func TestCmdInfoByExchangeInDotFormat(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/rabbitmq_rest_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestGetAllResources(t *testing.T) {
assert.Equal(t, 1, len(all.Connections))
assert.Equal(t, 12, len(all.Exchanges))
assert.Equal(t, 8, len(all.Queues))
assert.Equal(t, 16, len(all.Bindings))
assert.Equal(t, 17, len(all.Bindings))
assert.Equal(t, 2, len(all.Consumers))
}

Expand Down
11 changes: 11 additions & 0 deletions pkg/testcommon/rabbitmq_rest_api_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,17 @@ const (
},
"properties_key": "topic-q2"
},
{
"source": "test-topic",
"vhost": "/",
"destination": "test-topic",
"destination_type": "exchange",
"routing_key": "",
"arguments": {
},
"properties_key": "~"
}
]`

Expand Down

0 comments on commit aa06467

Please sign in to comment.