From c606c1664f163752db5619853a2442da061127bc Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Wed, 20 Jan 2021 16:07:41 -0500 Subject: [PATCH] Add admin tasks for listing, setting, and deleting feature flags. --- .../textsecuregcm/WhisperServerService.java | 6 +++ .../workers/AbstractFeatureFlagTask.java | 31 ++++++++++++++ .../workers/DeleteFeatureFlagTask.java | 35 ++++++++++++++++ .../workers/ListFeatureFlagsTask.java | 24 +++++++++++ .../workers/SetFeatureFlagTask.java | 40 +++++++++++++++++++ .../workers/DeleteFeatureFlagTaskTest.java | 40 +++++++++++++++++++ .../workers/SetFeatureFlagTaskTest.java | 40 +++++++++++++++++++ 7 files changed, 216 insertions(+) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/workers/AbstractFeatureFlagTask.java create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTask.java create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/workers/ListFeatureFlagsTask.java create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTask.java create mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTaskTest.java create mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTaskTest.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 030224fc5..448ee01d1 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -145,12 +145,15 @@ import org.whispersystems.textsecuregcm.websocket.DeadLetterHandler; import org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener; import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator; import org.whispersystems.textsecuregcm.workers.CertificateCommand; +import org.whispersystems.textsecuregcm.workers.DeleteFeatureFlagTask; import org.whispersystems.textsecuregcm.workers.DeleteUserCommand; import org.whispersystems.textsecuregcm.workers.DisableRequestLoggingTask; import org.whispersystems.textsecuregcm.workers.EnableRequestLoggingTask; import org.whispersystems.textsecuregcm.workers.GetRedisCommandStatsCommand; import org.whispersystems.textsecuregcm.workers.GetRedisSlowlogCommand; +import org.whispersystems.textsecuregcm.workers.ListFeatureFlagsTask; import org.whispersystems.textsecuregcm.workers.SetCrawlerAccelerationTask; +import org.whispersystems.textsecuregcm.workers.SetFeatureFlagTask; import org.whispersystems.textsecuregcm.workers.VacuumCommand; import org.whispersystems.textsecuregcm.workers.ZkParamsCommand; import org.whispersystems.websocket.WebSocketResourceProviderFactory; @@ -460,6 +463,9 @@ public class WhisperServerService extends Application out.println(flag + ": " + active)); + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTask.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTask.java new file mode 100644 index 000000000..36f260444 --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTask.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.workers; + +import org.whispersystems.textsecuregcm.storage.FeatureFlagsManager; + +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class DeleteFeatureFlagTask extends AbstractFeatureFlagTask { + + public DeleteFeatureFlagTask(final FeatureFlagsManager featureFlagsManager) { + super("delete-feature-flag", featureFlagsManager); + } + + @Override + public void execute(final Map> parameters, final PrintWriter out) { + if (parameters.containsKey("flag")) { + for (final String flag : parameters.getOrDefault("flag", Collections.emptyList())) { + out.println("Deleting feature flag: " + flag); + getFeatureFlagsManager().deleteFeatureFlag(flag); + } + + out.println(); + printFeatureFlags(out); + } else { + out.println("Usage: delete-feature-flag?flag=FLAG_NAME[&flag=FLAG_NAME2&flag=...]"); + } + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/ListFeatureFlagsTask.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/ListFeatureFlagsTask.java new file mode 100644 index 000000000..ef6fb254f --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/ListFeatureFlagsTask.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.workers; + +import org.whispersystems.textsecuregcm.storage.FeatureFlagsManager; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; + +public class ListFeatureFlagsTask extends AbstractFeatureFlagTask { + + public ListFeatureFlagsTask(final FeatureFlagsManager featureFlagsManager) { + super("list-feature-flags", featureFlagsManager); + } + + @Override + public void execute(final Map> parameters, final PrintWriter out) { + printFeatureFlags(out); + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTask.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTask.java new file mode 100644 index 000000000..1fce6b844 --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTask.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.workers; + +import org.whispersystems.textsecuregcm.storage.FeatureFlagsManager; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class SetFeatureFlagTask extends AbstractFeatureFlagTask { + + public SetFeatureFlagTask(final FeatureFlagsManager featureFlagsManager) { + super("set-feature-flag", featureFlagsManager); + } + + @Override + public void execute(final Map> parameters, final PrintWriter out) { + final Optional maybeFlag = Optional.ofNullable(parameters.get("flag")) + .flatMap(values -> values.stream().findFirst()); + + final Optional maybeActive = Optional.ofNullable(parameters.get("active")) + .flatMap(values -> values.stream().findFirst()) + .map(Boolean::valueOf); + + if (maybeFlag.isPresent() && maybeActive.isPresent()) { + getFeatureFlagsManager().setFeatureFlag(maybeFlag.get(), maybeActive.get()); + + out.format("Set %s to %s\n", maybeFlag.get(), maybeActive.get()); + out.println(); + printFeatureFlags(out); + } else { + out.println("Usage: set-feature-flag?flag=FLAG_NAME&value=[true|false]"); + } + } +} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTaskTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTaskTest.java new file mode 100644 index 000000000..208f103f1 --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/workers/DeleteFeatureFlagTaskTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.workers; + +import org.junit.Before; +import org.junit.Test; +import org.whispersystems.textsecuregcm.storage.FeatureFlagsManager; + +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DeleteFeatureFlagTaskTest { + + private FeatureFlagsManager featureFlagsManager; + + @Before + public void setUp() { + featureFlagsManager = mock(FeatureFlagsManager.class); + + when(featureFlagsManager.getAllFlags()).thenReturn(Collections.emptyMap()); + } + + @Test + public void testExecute() { + final DeleteFeatureFlagTask task = new DeleteFeatureFlagTask(featureFlagsManager); + + task.execute(Map.of("flag", List.of("test-flag-1", "test-flag-2")), mock(PrintWriter.class)); + verify(featureFlagsManager).deleteFeatureFlag("test-flag-1"); + verify(featureFlagsManager).deleteFeatureFlag("test-flag-2"); + } +} \ No newline at end of file diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTaskTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTaskTest.java new file mode 100644 index 000000000..33fec542d --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/workers/SetFeatureFlagTaskTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.workers; + +import org.junit.Before; +import org.junit.Test; +import org.whispersystems.textsecuregcm.storage.FeatureFlagsManager; + +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class SetFeatureFlagTaskTest { + + private FeatureFlagsManager featureFlagsManager; + + @Before + public void setUp() { + featureFlagsManager = mock(FeatureFlagsManager.class); + + when(featureFlagsManager.getAllFlags()).thenReturn(Collections.emptyMap()); + } + + @Test + public void testExecute() { + final SetFeatureFlagTask task = new SetFeatureFlagTask(featureFlagsManager); + + task.execute(Map.of("flag", List.of("test-flag"), "active", List.of("true")), mock(PrintWriter.class)); + + verify(featureFlagsManager).setFeatureFlag("test-flag", true); + } +} \ No newline at end of file