|
33 | 33 |
|
34 | 34 | -export([is_supported/1, is_supported/2, |
35 | 35 | enable/1, |
| 36 | + enable_default/0, |
36 | 37 | check_node_compatibility/1, |
37 | 38 | sync_cluster/0, |
38 | | - refresh_after_app_load/0]). |
| 39 | + refresh_after_app_load/0, |
| 40 | + get_forced_feature_flag_names/0]). |
39 | 41 |
|
40 | 42 | %% Internal use only. |
41 | 43 | -export([start/0, |
@@ -96,6 +98,25 @@ enable(FeatureNames) when is_list(FeatureNames) -> |
96 | 98 | #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), |
97 | 99 | gen_statem:call(?LOCAL_NAME, {enable, FeatureNames}). |
98 | 100 |
|
| 101 | +enable_default() -> |
| 102 | + ?LOG_DEBUG( |
| 103 | + "Feature flags: configure initial feature flags state", |
| 104 | + [], |
| 105 | + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), |
| 106 | + case erlang:whereis(?LOCAL_NAME) of |
| 107 | + Pid when is_pid(Pid) -> |
| 108 | + %% The function is called while `rabbit' is running. |
| 109 | + gen_statem:call(?LOCAL_NAME, enable_default); |
| 110 | + undefined -> |
| 111 | + %% The function is called while `rabbit' is stopped. We need to |
| 112 | + %% start a one-off controller, again to make sure concurrent |
| 113 | + %% changes are blocked. |
| 114 | + {ok, Pid} = start_link(), |
| 115 | + Ret = gen_statem:call(Pid, enable_default), |
| 116 | + gen_statem:stop(Pid), |
| 117 | + Ret |
| 118 | + end. |
| 119 | + |
99 | 120 | check_node_compatibility(RemoteNode) -> |
100 | 121 | ThisNode = node(), |
101 | 122 | ?LOG_DEBUG( |
@@ -248,6 +269,8 @@ updating_feature_flag_states( |
248 | 269 |
|
249 | 270 | proceed_with_task({enable, FeatureNames}) -> |
250 | 271 | enable_task(FeatureNames); |
| 272 | +proceed_with_task(enable_default) -> |
| 273 | + enable_default_task(); |
251 | 274 | proceed_with_task(sync_cluster) -> |
252 | 275 | sync_cluster_task(); |
253 | 276 | proceed_with_task(refresh_after_app_load) -> |
@@ -451,6 +474,122 @@ enable_task(FeatureNames) -> |
451 | 474 | {error, missing_clustered_nodes} |
452 | 475 | end. |
453 | 476 |
|
| 477 | +enable_default_task() -> |
| 478 | + FeatureNames = get_forced_feature_flag_names(), |
| 479 | + case FeatureNames of |
| 480 | + undefined -> |
| 481 | + ?LOG_DEBUG( |
| 482 | + "Feature flags: starting an unclustered node for the first " |
| 483 | + "time: all stable feature flags will be enabled by default", |
| 484 | + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), |
| 485 | + {ok, Inventory} = collect_inventory_on_nodes([node()]), |
| 486 | + #{feature_flags := FeatureFlags} = Inventory, |
| 487 | + StableFeatureNames = |
| 488 | + maps:fold( |
| 489 | + fun |
| 490 | + (FeatureName, #{stability := stable}, Acc) -> |
| 491 | + [FeatureName | Acc]; |
| 492 | + (_FeatureName, _FeatureProps, Acc) -> |
| 493 | + Acc |
| 494 | + end, [], FeatureFlags), |
| 495 | + enable_many(Inventory, StableFeatureNames); |
| 496 | + [] -> |
| 497 | + ?LOG_DEBUG( |
| 498 | + "Feature flags: starting an unclustered node for the first " |
| 499 | + "time: all feature flags are forcibly left disabled from " |
| 500 | + "the $RABBITMQ_FEATURE_FLAGS environment variable", |
| 501 | + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), |
| 502 | + ok; |
| 503 | + _ -> |
| 504 | + ?LOG_DEBUG( |
| 505 | + "Feature flags: starting an unclustered node for the first " |
| 506 | + "time: only the following feature flags specified in the " |
| 507 | + "$RABBITMQ_FEATURE_FLAGS environment variable will be enabled: " |
| 508 | + "~tp", |
| 509 | + [FeatureNames], |
| 510 | + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), |
| 511 | + {ok, Inventory} = collect_inventory_on_nodes([node()]), |
| 512 | + enable_many(Inventory, FeatureNames) |
| 513 | + end. |
| 514 | + |
| 515 | +-spec get_forced_feature_flag_names() -> Ret when |
| 516 | + Ret :: FeatureNames | undefined, |
| 517 | + FeatureNames :: [rabbit_feature_flags:feature_name()]. |
| 518 | +%% @doc Returns the (possibly empty) list of feature flags the user wants to |
| 519 | +%% enable out-of-the-box when starting a node for the first time. |
| 520 | +%% |
| 521 | +%% Without this, the default is to enable all the supported stable feature |
| 522 | +%% flags. |
| 523 | +%% |
| 524 | +%% There are two ways to specify that list: |
| 525 | +%% <ol> |
| 526 | +%% <li>Using the `$RABBITMQ_FEATURE_FLAGS' environment variable; for |
| 527 | +%% instance `RABBITMQ_FEATURE_FLAGS=quorum_queue,mnevis'.</li> |
| 528 | +%% <li>Using the `forced_feature_flags_on_init' configuration parameter; |
| 529 | +%% for instance |
| 530 | +%% `{rabbit, [{forced_feature_flags_on_init, [quorum_queue, mnevis]}]}'.</li> |
| 531 | +%% </ol> |
| 532 | +%% |
| 533 | +%% The environment variable has precedence over the configuration parameter. |
| 534 | +%% |
| 535 | +%% @private |
| 536 | + |
| 537 | +get_forced_feature_flag_names() -> |
| 538 | + Ret = case get_forced_feature_flag_names_from_env() of |
| 539 | + undefined -> get_forced_feature_flag_names_from_config(); |
| 540 | + List -> List |
| 541 | + end, |
| 542 | + case Ret of |
| 543 | + undefined -> |
| 544 | + ok; |
| 545 | + [] -> |
| 546 | + ?LOG_INFO( |
| 547 | + "Feature flags: automatic enablement of feature flags " |
| 548 | + "disabled (i.e. none will be enabled automatically)", |
| 549 | + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}); |
| 550 | + _ -> |
| 551 | + ?LOG_INFO( |
| 552 | + "Feature flags: automatic enablement of feature flags " |
| 553 | + "limited to the following list: ~tp", |
| 554 | + [Ret], |
| 555 | + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}) |
| 556 | + end, |
| 557 | + Ret. |
| 558 | + |
| 559 | +-spec get_forced_feature_flag_names_from_env() -> Ret when |
| 560 | + Ret :: FeatureNames | undefined, |
| 561 | + FeatureNames :: [rabbit_feature_flags:feature_name()]. |
| 562 | +%% @private |
| 563 | + |
| 564 | +get_forced_feature_flag_names_from_env() -> |
| 565 | + case rabbit_prelaunch:get_context() of |
| 566 | + #{forced_feature_flags_on_init := ForcedFFs} |
| 567 | + when is_list(ForcedFFs) -> |
| 568 | + ForcedFFs; |
| 569 | + _ -> |
| 570 | + undefined |
| 571 | + end. |
| 572 | + |
| 573 | +-spec get_forced_feature_flag_names_from_config() -> Ret when |
| 574 | + Ret :: FeatureNames | undefined, |
| 575 | + FeatureNames :: [rabbit_feature_flags:feature_name()]. |
| 576 | +%% @private |
| 577 | + |
| 578 | +get_forced_feature_flag_names_from_config() -> |
| 579 | + Value = application:get_env( |
| 580 | + rabbit, forced_feature_flags_on_init, undefined), |
| 581 | + case Value of |
| 582 | + undefined -> |
| 583 | + Value; |
| 584 | + _ when is_list(Value) -> |
| 585 | + case lists:all(fun is_atom/1, Value) of |
| 586 | + true -> Value; |
| 587 | + false -> undefined |
| 588 | + end; |
| 589 | + _ -> |
| 590 | + undefined |
| 591 | + end. |
| 592 | + |
454 | 593 | -spec sync_cluster_task() -> Ret when |
455 | 594 | Ret :: ok | {error, Reason}, |
456 | 595 | Reason :: term(). |
|
0 commit comments