|
30 | 30 | all() -> |
31 | 31 | [ |
32 | 32 | {group, essential}, |
33 | | - {group, cluster_size_3} |
| 33 | + {group, cluster_size_3}, |
| 34 | + {group, rolling_upgrade} |
34 | 35 | ]. |
35 | 36 |
|
36 | 37 | groups() -> |
37 | 38 | [ |
38 | 39 | {essential, [], essential()}, |
39 | 40 | {cluster_size_3, [], [max_hops]}, |
| 41 | + {rolling_upgrade, [], [child_id_format]}, |
40 | 42 | {cycle_protection, [], [ |
41 | 43 | %% TBD: port from v3.10.x in an Erlang 25-compatible way |
42 | 44 | ]}, |
@@ -96,6 +98,12 @@ init_per_group(cluster_size_3 = Group, Config) -> |
96 | 98 | {rmq_nodes_count, 3} |
97 | 99 | ]), |
98 | 100 | init_per_group1(Group, Config1); |
| 101 | +init_per_group(rolling_upgrade = Group, Config) -> |
| 102 | + Config1 = rabbit_ct_helpers:set_config(Config, [ |
| 103 | + {rmq_nodes_count, 5}, |
| 104 | + {rmq_nodes_clustered, false} |
| 105 | + ]), |
| 106 | + init_per_group1(Group, Config1); |
99 | 107 | init_per_group(Group, Config) -> |
100 | 108 | init_per_group1(Group, Config). |
101 | 109 |
|
@@ -539,6 +547,119 @@ lookup_exchange_status(Config) -> |
539 | 547 | [key, uri, status, timestamp, id, supervisor, upstream]), |
540 | 548 |
|
541 | 549 | clean_up_federation_related_bits(Config). |
| 550 | + |
| 551 | +child_id_format(Config) -> |
| 552 | + [UpstreamNode, |
| 553 | + OldNodeA, |
| 554 | + NewNodeB, |
| 555 | + OldNodeC, |
| 556 | + NewNodeD] = rabbit_ct_broker_helpers:get_node_configs( |
| 557 | + Config, nodename), |
| 558 | + |
| 559 | + %% Create a cluster with the nodes running the old version of RabbitMQ in |
| 560 | + %% mixed-version testing. |
| 561 | + %% |
| 562 | + %% Note: we build this on the assumption that `rabbit_ct_broker_helpers' |
| 563 | + %% starts nodes this way: |
| 564 | + %% Node 1: the primary copy of RabbitMQ the test is started from |
| 565 | + %% Node 2: the secondary umbrella (if any) |
| 566 | + %% Node 3: the primary copy |
| 567 | + %% Node 4: the secondary umbrella |
| 568 | + %% ... |
| 569 | + %% |
| 570 | + %% Therefore, `UpstreamNode' will use the primary copy, `OldNodeA' the |
| 571 | + %% secondary umbrella, `NewNodeB' the primary copy, and so on. |
| 572 | + Config1 = rabbit_ct_broker_helpers:cluster_nodes( |
| 573 | + Config, [OldNodeA, OldNodeC]), |
| 574 | + |
| 575 | + %% Prepare the whole federated exchange on that old cluster. |
| 576 | + UpstreamName = <<"fed_on_upgrade">>, |
| 577 | + rabbit_ct_broker_helpers:set_parameter( |
| 578 | + Config1, OldNodeA, <<"federation-upstream">>, UpstreamName, |
| 579 | + [ |
| 580 | + {<<"uri">>, rabbit_ct_broker_helpers:node_uri(Config1, UpstreamNode)} |
| 581 | + ]), |
| 582 | + |
| 583 | + rabbit_ct_broker_helpers:set_policy( |
| 584 | + Config1, OldNodeA, |
| 585 | + <<"fed_on_upgrade_policy">>, <<"^fed_">>, <<"all">>, |
| 586 | + [ |
| 587 | + {<<"federation-upstream-pattern">>, UpstreamName} |
| 588 | + ]), |
| 589 | + |
| 590 | + XName = <<"fed_ex_on_upgrade_cluster">>, |
| 591 | + X = exchange_declare_method(XName, <<"direct">>), |
| 592 | + {Conn1, Ch1} = rabbit_ct_client_helpers:open_connection_and_channel( |
| 593 | + Config1, OldNodeA), |
| 594 | + ?assertEqual({'exchange.declare_ok'}, declare_exchange(Ch1, X)), |
| 595 | + rabbit_ct_client_helpers:close_channel(Ch1), |
| 596 | + rabbit_ct_client_helpers:close_connection(Conn1), |
| 597 | + |
| 598 | + %% Verify the format of the child ID. In the main branch, the format was |
| 599 | + %% temporarily a size-2 tuple with a list as the first element. This was |
| 600 | + %% not kept later and the original ID format is used in old and new nodes. |
| 601 | + [{Id, _, _, _}] = rabbit_ct_broker_helpers:rpc( |
| 602 | + Config1, OldNodeA, |
| 603 | + mirrored_supervisor, which_children, |
| 604 | + [rabbit_federation_exchange_link_sup_sup]), |
| 605 | + case Id of |
| 606 | + %% This is the format we expect everywhere. |
| 607 | + #exchange{name = #resource{name = XName}} -> |
| 608 | + %% Verify that the supervisors exist on all nodes. |
| 609 | + lists:foreach( |
| 610 | + fun(Node) -> |
| 611 | + ?assertMatch( |
| 612 | + [{#exchange{name = #resource{name = XName}}, |
| 613 | + _, _, _}], |
| 614 | + rabbit_ct_broker_helpers:rpc( |
| 615 | + Config1, Node, |
| 616 | + mirrored_supervisor, which_children, |
| 617 | + [rabbit_federation_exchange_link_sup_sup])) |
| 618 | + end, [OldNodeA, OldNodeC]), |
| 619 | + |
| 620 | + %% Simulate a rolling upgrade by: |
| 621 | + %% 1. adding new nodes to the old cluster |
| 622 | + %% 2. stopping the old nodes |
| 623 | + %% |
| 624 | + %% After that, the supervisors run on the new code. |
| 625 | + Config2 = rabbit_ct_broker_helpers:cluster_nodes( |
| 626 | + Config1, [OldNodeA, NewNodeB, NewNodeD]), |
| 627 | + ok = rabbit_ct_broker_helpers:stop_broker(Config2, OldNodeA), |
| 628 | + ok = rabbit_ct_broker_helpers:reset_node(Config1, OldNodeA), |
| 629 | + ok = rabbit_ct_broker_helpers:stop_broker(Config2, OldNodeC), |
| 630 | + ok = rabbit_ct_broker_helpers:reset_node(Config2, OldNodeC), |
| 631 | + |
| 632 | + %% Verify that the supervisors still use the same IDs. |
| 633 | + lists:foreach( |
| 634 | + fun(Node) -> |
| 635 | + ?assertMatch( |
| 636 | + [{#exchange{name = #resource{name = XName}}, |
| 637 | + _, _, _}], |
| 638 | + rabbit_ct_broker_helpers:rpc( |
| 639 | + Config2, Node, |
| 640 | + mirrored_supervisor, which_children, |
| 641 | + [rabbit_federation_exchange_link_sup_sup])) |
| 642 | + end, [NewNodeB, NewNodeD]), |
| 643 | + |
| 644 | + %% Delete the exchange: it should work because the ID format is the |
| 645 | + %% one expected. |
| 646 | + %% |
| 647 | + %% During the transient period where the ID format was changed, |
| 648 | + %% this would crash with a badmatch because the running |
| 649 | + %% supervisor's ID would not match the content of the database. |
| 650 | + {Conn2, Ch2} = rabbit_ct_client_helpers:open_connection_and_channel( |
| 651 | + Config2, NewNodeB), |
| 652 | + ?assertEqual({'exchange.delete_ok'}, delete_exchange(Ch2, XName)), |
| 653 | + rabbit_ct_client_helpers:close_channel(Ch2), |
| 654 | + rabbit_ct_client_helpers:close_connection(Conn2); |
| 655 | + |
| 656 | + %% This is the transient format we are not interested in as it only |
| 657 | + %% lived in a development branch. |
| 658 | + {List, #exchange{name = #resource{name = XName}}} |
| 659 | + when is_list(List) -> |
| 660 | + {skip, "Testcase skipped with the transiently changed ID format"} |
| 661 | + end. |
| 662 | + |
542 | 663 | %% |
543 | 664 | %% Test helpers |
544 | 665 | %% |
|
0 commit comments