Skip to content

Commit 86f29b4

Browse files
michaelklishinmergify[bot]
authored andcommitted
cli enable_feature_flag: alias --experimental as --opt-in
--experimental is no longer particularly fair to Khepri, which is not enabled by default because of its enormous scope, and because once enabled, it cannot be disabled. --opt-in would be a better name but --experimental remains for backwards compatiblity. When both are specified, we consider that the user opts in if at least one of the flags is set to true. (cherry picked from commit 47210c8)
1 parent fa2f2c2 commit 86f29b4

File tree

2 files changed

+97
-34
lines changed

2 files changed

+97
-34
lines changed

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/enable_feature_flag_command.ex

Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,58 +7,65 @@
77
defmodule RabbitMQ.CLI.Ctl.Commands.EnableFeatureFlagCommand do
88
@behaviour RabbitMQ.CLI.CommandBehaviour
99

10-
def switches(), do: [experimental: :boolean]
11-
def aliases(), do: [e: :experimental]
10+
def switches(), do: [experimental: :boolean, opt_in: :boolean]
11+
def aliases(), do: [e: :experimental, o: :opt_in]
1212

13-
def merge_defaults(args, opts), do: { args, Map.merge(%{experimental: false}, opts) }
13+
def merge_defaults(args, opts), do: { args, Map.merge(%{experimental: false, opt_in: false}, opts) }
1414

1515
def validate([], _opts), do: {:validation_failure, :not_enough_args}
1616
def validate([_ | _] = args, _opts) when length(args) > 1, do: {:validation_failure, :too_many_args}
1717

1818
def validate([""], _opts),
19-
do: {:validation_failure, {:bad_argument, "feature_flag cannot be an empty string."}}
19+
do: {:validation_failure, {:bad_argument, "feature flag (or group) name cannot be an empty string"}}
2020

2121
def validate([_], _opts), do: :ok
2222

2323
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
2424

25-
def run(["all"], %{node: node_name, experimental: experimental}) do
26-
case experimental do
27-
true ->
28-
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage(), "`--experimental` flag is not allowed when enabling all feature flags.\nUse --experimental with a specific feature flag if you want to enable an experimental feature."}
29-
false ->
30-
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :enable_all, []) do
31-
{:badrpc, _} = err -> err
32-
other -> other
33-
end
34-
end
25+
def run(["all"], %{node: node_name, opt_in: opt_in, experimental: experimental}) do
26+
has_opted_in = (opt_in || experimental)
27+
enable_all(node_name, has_opted_in)
3528
end
3629

37-
def run([feature_flag], %{node: node_name, experimental: experimental}) do
38-
case {experimental, :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :get_stability, [
39-
String.to_atom(feature_flag)
40-
])} do
41-
{_, {:badrpc, _} = err} -> err
42-
{false, :experimental} ->
43-
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage(), "Feature flag #{feature_flag} is experimental. If you understand the risk, use --experimental to enable it."}
44-
_ ->
45-
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :enable, [
46-
String.to_atom(feature_flag)
47-
]) do
48-
{:badrpc, _} = err -> err
49-
other -> other
50-
end
51-
end
30+
def run(["all"], %{node: node_name, opt_in: has_opted_in}) do
31+
enable_all(node_name, has_opted_in)
5232
end
5333

34+
def run(["all"], %{node: node_name, experimental: has_opted_in}) do
35+
enable_all(node_name, has_opted_in)
36+
end
37+
38+
def run(["all"], %{node: node_name}) do
39+
enable_all(node_name, false)
40+
end
41+
42+
43+
def run([feature_flag], %{node: node_name, opt_in: opt_in, experimental: experimental}) do
44+
has_opted_in = (opt_in || experimental)
45+
enable_one(node_name, feature_flag, has_opted_in)
46+
end
47+
48+
def run([feature_flag], %{node: node_name, opt_in: has_opted_in}) do
49+
enable_one(node_name, feature_flag, has_opted_in)
50+
end
51+
52+
def run([feature_flag], %{node: node_name, experimental: has_opted_in}) do
53+
enable_one(node_name, feature_flag, has_opted_in)
54+
end
55+
56+
def run([feature_flag], %{node: node_name}) do
57+
enable_one(node_name, feature_flag, false)
58+
end
59+
60+
5461
def output({:error, :unsupported}, %{node: node_name}) do
5562
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage(),
5663
"This feature flag is not supported by node #{node_name}"}
5764
end
5865

5966
use RabbitMQ.CLI.DefaultOutput
6067

61-
def usage, do: "enable_feature_flag [--experimental] <all | feature_flag>"
68+
def usage, do: "enable_feature_flag [--opt-in] <all | feature_flag>"
6269

6370
def usage_additional() do
6471
[
@@ -67,8 +74,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EnableFeatureFlagCommand do
6774
"name of the feature flag to enable, or \"all\" to enable all supported flags"
6875
],
6976
[
70-
"--experimental",
71-
"required to enable experimental feature flags (make sure you understand the risks!)"
77+
"--opt-in",
78+
"required to enable certain feature flags (those with vast scope or maturing)"
7279
]
7380
]
7481
end
@@ -81,4 +88,39 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EnableFeatureFlagCommand do
8188
def banner(["all"], _), do: "Enabling all feature flags ..."
8289

8390
def banner([feature_flag], _), do: "Enabling feature flag \"#{feature_flag}\" ..."
91+
92+
#
93+
# Implementation
94+
#
95+
96+
defp enable_all(node_name, has_opted_in) do
97+
case has_opted_in do
98+
true ->
99+
msg = "`--opt-in` (aliased as `--experimental`) flag is not allowed when enabling all feature flags.\nUse --opt-in with a specific feature flag name if to enable an opt-in flag"
100+
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage(), msg}
101+
_ ->
102+
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :enable_all, []) do
103+
{:badrpc, _} = err -> err
104+
other -> other
105+
end
106+
end
107+
end
108+
109+
defp enable_one(node_name, feature_flag, has_opted_in) do
110+
case {has_opted_in, :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :get_stability, [
111+
String.to_atom(feature_flag)
112+
])} do
113+
{_, {:badrpc, _} = err} -> err
114+
{false, :experimental} ->
115+
msg = "Feature flag #{feature_flag} requires the user to explicitly opt-in.\nUse --opt-in with a specific feature flag name if to enable an opt-in flag"
116+
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage(), msg}
117+
_ ->
118+
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :enable, [
119+
String.to_atom(feature_flag)
120+
]) do
121+
{:badrpc, _} = err -> err
122+
other -> other
123+
end
124+
end
125+
end
84126
end

deps/rabbitmq_cli/test/ctl/enable_feature_flag_test.exs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,27 @@ defmodule EnableFeatureFlagCommandTest do
6767
assert list_feature_flags(:enabled) |> Map.has_key?(context[:feature_flag])
6868
end
6969

70-
test "run: attempt to use an unreachable node returns a nodedown" do
70+
test "run: attempt to use an unreachable node with --opt-in returns a nodedown" do
71+
opts = %{node: :jake@thedog, timeout: 200, opt_in: false}
72+
assert match?({:badrpc, _}, @command.run(["na"], opts))
73+
end
74+
75+
test "run: attempt to use an unreachable node with --experimental returns a nodedown" do
7176
opts = %{node: :jake@thedog, timeout: 200, experimental: false}
7277
assert match?({:badrpc, _}, @command.run(["na"], opts))
7378
end
7479

75-
test "run: enabling an experimental flag requires '--experimental'", context do
80+
test "run: enabling an experimental flag requires '--opt-in'", context do
81+
experimental_flag = Atom.to_string(context[:experimental_flag])
82+
assert match?(
83+
{:error, @usage_exit_code, _},
84+
@command.run([experimental_flag], context[:opts])
85+
)
86+
opts = Map.put(context[:opts], :opt_in, true)
87+
assert @command.run([experimental_flag], opts) == :ok
88+
end
89+
90+
test "run: enabling an experimental flag accepts '--experimental'", context do
7691
experimental_flag = Atom.to_string(context[:experimental_flag])
7792
assert match?(
7893
{:error, @usage_exit_code, _},
@@ -94,6 +109,12 @@ defmodule EnableFeatureFlagCommandTest do
94109
assert list_feature_flags(:enabled) |> Map.has_key?(context[:feature_flag])
95110
end
96111

112+
test "run: enabling all feature flags with '--opt-in' returns an error", context do
113+
enable_feature_flag(context[:feature_flag])
114+
opts = Map.put(context[:opts], :opt_in, true)
115+
assert match?({:error, @usage_exit_code, _}, @command.run(["all"], opts))
116+
end
117+
97118
test "run: enabling all feature flags with '--experimental' returns an error", context do
98119
enable_feature_flag(context[:feature_flag])
99120
opts = Map.put(context[:opts], :experimental, true)

0 commit comments

Comments
 (0)