@@ -675,48 +675,83 @@ enable_task(FeatureNames) ->
675675 end .
676676
677677enable_default_task () ->
678- FeatureNames = get_forced_feature_flag_names (),
679- case FeatureNames of
680- undefined ->
678+ case get_forced_feature_flag_names () of
679+ {ok , undefined } ->
681680 ? LOG_DEBUG (
682681 " Feature flags: starting an unclustered node for the first "
683682 " time: all stable feature flags will be enabled by default" ,
684683 #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
685684 {ok , Inventory } = collect_inventory_on_nodes ([node ()]),
686- #{feature_flags := FeatureFlags } = Inventory ,
687- StableFeatureNames =
688- maps :fold (
689- fun (FeatureName , FeatureProps , Acc ) ->
690- Stability = rabbit_feature_flags :get_stability (
691- FeatureProps ),
692- case Stability of
693- stable -> [FeatureName | Acc ];
694- _ -> Acc
695- end
696- end , [], FeatureFlags ),
685+ StableFeatureNames = get_stable_feature_flags (Inventory ),
697686 enable_many (Inventory , StableFeatureNames );
698- [] ->
687+ { ok , []} ->
699688 ? LOG_DEBUG (
700689 " Feature flags: starting an unclustered node for the first "
701690 " time: all feature flags are forcibly left disabled from "
702691 " the $RABBITMQ_FEATURE_FLAGS environment variable" ,
703692 #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
704693 ok ;
705- _ ->
694+ { ok , FeatureNames } when is_list ( FeatureNames ) ->
706695 ? LOG_DEBUG (
707696 " Feature flags: starting an unclustered node for the first "
708697 " time: only the following feature flags specified in the "
709698 " $RABBITMQ_FEATURE_FLAGS environment variable will be enabled: "
710- " ~tp " ,
699+ " ~0tp " ,
711700 [FeatureNames ],
712701 #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
713702 {ok , Inventory } = collect_inventory_on_nodes ([node ()]),
714- enable_many (Inventory , FeatureNames )
703+ enable_many (Inventory , FeatureNames );
704+ {ok , {rel , Plus , Minus }} ->
705+ ? LOG_DEBUG (
706+ " Feature flags: starting an unclustered node for the first "
707+ " time: all stable feature flags will be enabled, after "
708+ " applying changes from $RABBITMQ_FEATURE_FLAGS: adding ~0tp , "
709+ " skipping ~0tp " ,
710+ [Plus , Minus ],
711+ #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
712+ {ok , Inventory } = collect_inventory_on_nodes ([node ()]),
713+ StableFeatureNames = get_stable_feature_flags (Inventory ),
714+ Unsupported = lists :filter (
715+ fun (FeatureName ) ->
716+ not is_known_and_supported (
717+ Inventory , FeatureName )
718+ end , Minus ),
719+ case Unsupported of
720+ [] ->
721+ FeatureNames = (StableFeatureNames -- Minus ) ++ Plus ,
722+ enable_many (Inventory , FeatureNames );
723+ _ ->
724+ ? LOG_ERROR (
725+ " Feature flags: unsupported feature flags to skip in "
726+ " $RABBITMQ_FEATURE_FLAGS: ~0tp " ,
727+ [Unsupported ],
728+ #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
729+ {error , unsupported }
730+ end ;
731+ {error , syntax_error_in_envvar } = Error ->
732+ ? LOG_DEBUG (
733+ " Feature flags: invalid mix of `feature_flag` and "
734+ " `+/-feature_flag` in $RABBITMQ_FEATURE_FLAGS" ,
735+ #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
736+ Error
715737 end .
716738
739+ get_stable_feature_flags (#{feature_flags := FeatureFlags }) ->
740+ maps :fold (
741+ fun (FeatureName , FeatureProps , Acc ) ->
742+ Stability = rabbit_feature_flags :get_stability (FeatureProps ),
743+ case Stability of
744+ stable -> [FeatureName | Acc ];
745+ _ -> Acc
746+ end
747+ end , [], FeatureFlags ).
748+
717749-spec get_forced_feature_flag_names () -> Ret when
718- Ret :: FeatureNames | undefined ,
719- FeatureNames :: [rabbit_feature_flags :feature_name ()].
750+ Ret :: {ok , Abs | Rel | undefined } | {error , syntax_error_in_envvar },
751+ Abs :: [rabbit_feature_flags :feature_name ()],
752+ Rel :: {rel ,
753+ [rabbit_feature_flags :feature_name ()],
754+ [rabbit_feature_flags :feature_name ()]}.
720755% % @doc Returns the (possibly empty) list of feature flags the user wants to
721756% % enable out-of-the-box when starting a node for the first time.
722757% %
@@ -737,59 +772,73 @@ enable_default_task() ->
737772% % @private
738773
739774get_forced_feature_flag_names () ->
740- Ret = case get_forced_feature_flag_names_from_env () of
741- undefined -> get_forced_feature_flag_names_from_config ();
742- List -> List
743- end ,
744- case Ret of
745- undefined ->
746- ok ;
747- [] ->
748- ? LOG_INFO (
749- " Feature flags: automatic enablement of feature flags "
750- " disabled (i.e. none will be enabled automatically)" ,
751- #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS });
752- _ ->
753- ? LOG_INFO (
754- " Feature flags: automatic enablement of feature flags "
755- " limited to the following list: ~tp " ,
756- [Ret ],
757- #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS })
758- end ,
759- Ret .
775+ case get_forced_feature_flag_names_from_env () of
776+ {ok , undefined } -> get_forced_feature_flag_names_from_config ();
777+ {ok , _ } = Ret -> Ret ;
778+ {error , _ } = Error -> Error
779+ end .
760780
761781-spec get_forced_feature_flag_names_from_env () -> Ret when
762- Ret :: FeatureNames | undefined ,
763- FeatureNames :: [rabbit_feature_flags :feature_name ()].
782+ Ret :: {ok , Abs | Rel | undefined } | {error , syntax_error_in_envvar },
783+ Abs :: [rabbit_feature_flags :feature_name ()],
784+ Rel :: {rel ,
785+ [rabbit_feature_flags :feature_name ()],
786+ [rabbit_feature_flags :feature_name ()]}.
764787% % @private
765788
766789get_forced_feature_flag_names_from_env () ->
767- case rabbit_prelaunch :get_context () of
768- #{forced_feature_flags_on_init := ForcedFFs }
769- when is_list (ForcedFFs ) ->
770- ForcedFFs ;
771- _ ->
772- undefined
790+ Value = case rabbit_prelaunch :get_context () of
791+ #{forced_feature_flags_on_init := ForcedFFs } -> ForcedFFs ;
792+ _ -> undefined
793+ end ,
794+ case Value of
795+ undefined ->
796+ {ok , Value };
797+ [] ->
798+ {ok , Value };
799+ [[Op | _ ] | _ ] when Op =:= $+ orelse Op =:= $- ->
800+ lists :foldr (
801+ fun
802+ ([$+ | NameS ], {ok , {rel , Plus , Minus }}) ->
803+ Name = list_to_atom (NameS ),
804+ Plus1 = [Name | Plus ],
805+ {ok , {rel , Plus1 , Minus }};
806+ ([$- | NameS ], {ok , {rel , Plus , Minus }}) ->
807+ Name = list_to_atom (NameS ),
808+ Minus1 = [Name | Minus ],
809+ {ok , {rel , Plus , Minus1 }};
810+ (_ , {error , _ } = Error ) ->
811+ Error ;
812+ (_ , _ ) ->
813+ {error , syntax_error_in_envvar }
814+ end , {ok , {rel , [], []}}, Value );
815+ _ when is_list (Value ) ->
816+ lists :foldr (
817+ fun
818+ (Name , {ok , Abs }) when is_atom (Name ) ->
819+ {ok , [Name | Abs ]};
820+ ([C | _ ] = NameS , {ok , Abs })
821+ when C =/= $+ andalso C =/= $- ->
822+ Name = list_to_atom (NameS ),
823+ {ok , [Name | Abs ]};
824+ (_ , {error , _ } = Error ) ->
825+ Error ;
826+ (_ , _ ) ->
827+ {error , syntax_error_in_envvar }
828+ end , {ok , []}, Value )
773829 end .
774830
775831- spec get_forced_feature_flag_names_from_config () -> Ret when
776- Ret :: FeatureNames | undefined ,
832+ Ret :: { ok , FeatureNames | undefined } ,
777833 FeatureNames :: [rabbit_feature_flags :feature_name ()].
778834% % @private
779835
780836get_forced_feature_flag_names_from_config () ->
781837 Value = application :get_env (
782838 rabbit , forced_feature_flags_on_init , undefined ),
783839 case Value of
784- undefined ->
785- Value ;
786- _ when is_list (Value ) ->
787- case lists :all (fun is_atom /1 , Value ) of
788- true -> Value ;
789- false -> undefined
790- end ;
791- _ ->
792- undefined
840+ undefined -> {ok , Value };
841+ _ when is_list (Value ) -> {ok , Value }
793842 end .
794843
795844- spec sync_cluster_task () -> Ret when
@@ -914,7 +963,7 @@ enable_if_supported(#{states_per_node := _} = Inventory, FeatureName) ->
914963 #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
915964 enable_with_registry_locked (Inventory , FeatureName );
916965 false ->
917- ? LOG_DEBUG (
966+ ? LOG_ERROR (
918967 " Feature flags: `~ts `: unsupported; aborting" ,
919968 [FeatureName ],
920969 #{domain => ? RMQLOG_DOMAIN_FEAT_FLAGS }),
0 commit comments