You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2018/05/17 13:01:51 UTC

[geode] branch develop updated: GEODE-4858: Convert DestroyIndexCommand to ResultModel and new cluster persistence API (#1966)

This is an automated email from the ASF dual-hosted git repository.

jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 085078f  GEODE-4858: Convert DestroyIndexCommand to ResultModel and new cluster persistence API (#1966)
085078f is described below

commit 085078f942dcc3cae24a445e7a8f8685ecd25a94
Author: Jens Deppe <jd...@pivotal.io>
AuthorDate: Thu May 17 06:01:45 2018 -0700

    GEODE-4858: Convert DestroyIndexCommand to ResultModel and new cluster persistence API (#1966)
---
 .../cli/commands/DestroyIndexIfExistsTest.java     |   2 +-
 .../internal/cli/commands/DestroyIndexCommand.java |  67 ++++++---
 .../cli/functions/DestroyIndexFunction.java        |  43 +++---
 .../cli/commands/CreateIndexCommandDUnitTest.java  |   6 +-
 .../commands/DestroyIndexCommandsDUnitTest.java    | 164 +++++++++++++++------
 .../cli/commands/IndexCommandsIntegrationTest.java |  14 +-
 6 files changed, 201 insertions(+), 95 deletions(-)

diff --git a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexIfExistsTest.java b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexIfExistsTest.java
index 65027c1..f17f78c 100644
--- a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexIfExistsTest.java
+++ b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexIfExistsTest.java
@@ -39,6 +39,6 @@ public class DestroyIndexIfExistsTest {
         GfshScript.of("start locator --name=locator", "start server --name=server",
             "sleep --time=1", "destroy index --name=i1 --if-exists=true").execute(gfsh);
 
-    assertThat(execution.getOutputText()).contains("Index i1 not found - skipped");
+    assertThat(execution.getOutputText()).contains("IGNORED", "Index named \"i1\" not found");
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommand.java
index 210d32d..5509878 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommand.java
@@ -22,29 +22,31 @@ import org.apache.commons.lang.ArrayUtils;
 import org.springframework.shell.core.annotation.CliCommand;
 import org.springframework.shell.core.annotation.CliOption;
 
+import org.apache.geode.cache.configuration.CacheConfig;
+import org.apache.geode.cache.configuration.CacheElement;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.distributed.ConfigurationPersistenceService;
 import org.apache.geode.distributed.DistributedMember;
-import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
 import org.apache.geode.internal.lang.StringUtils;
 import org.apache.geode.management.cli.CliMetaData;
 import org.apache.geode.management.cli.ConverterHint;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.domain.IndexInfo;
+import org.apache.geode.management.cli.SingleGfshCommand;
+import org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.functions.DestroyIndexFunction;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.ResultBuilder;
-import org.apache.geode.management.internal.configuration.domain.XmlEntity;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
-public class DestroyIndexCommand extends InternalGfshCommand {
+public class DestroyIndexCommand extends SingleGfshCommand {
   private static final DestroyIndexFunction destroyIndexFunction = new DestroyIndexFunction();
 
   @CliCommand(value = CliStrings.DESTROY_INDEX, help = CliStrings.DESTROY_INDEX__HELP)
   @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.QUERY)
-  public Result destroyIndex(
+  public ResultModel destroyIndex(
       @CliOption(key = CliStrings.DESTROY_INDEX__NAME, unspecifiedDefaultValue = "",
           help = CliStrings.DESTROY_INDEX__NAME__HELP) final String indexName,
       @CliOption(key = CliStrings.DESTROY_INDEX__REGION, optionContext = ConverterHint.REGION_PATH,
@@ -60,7 +62,7 @@ public class DestroyIndexCommand extends InternalGfshCommand {
 
     if (StringUtils.isBlank(indexName) && StringUtils.isBlank(regionPath)
         && ArrayUtils.isEmpty(group) && ArrayUtils.isEmpty(memberNameOrID)) {
-      return ResultBuilder.createUserErrorResult(
+      return ResultModel.createError(
           CliStrings.format(CliStrings.PROVIDE_ATLEAST_ONE_OPTION, CliStrings.DESTROY_INDEX));
     }
 
@@ -68,24 +70,53 @@ public class DestroyIndexCommand extends InternalGfshCommand {
     if (regionPath != null) {
       regionName = regionPath.startsWith("/") ? regionPath.substring(1) : regionPath;
     }
-    IndexInfo indexInfo = new IndexInfo(indexName, regionName);
-    indexInfo.setIfExists(ifExists);
+
+    RegionConfig.Index indexInfo = new RegionConfig.Index();
+    indexInfo.setName(indexName);
+    indexInfo.setFromClause(regionName);
+
     Set<DistributedMember> targetMembers = findMembers(group, memberNameOrID);
 
     if (targetMembers.isEmpty()) {
-      return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
+      return ResultModel.createError(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
     }
 
     List<CliFunctionResult> funcResults =
         executeAndGetFunctionResult(destroyIndexFunction, indexInfo, targetMembers);
-    Result result = ResultBuilder.buildResult(funcResults);
-    XmlEntity xmlEntity = findXmlEntity(funcResults);
+    ResultModel result = ResultModel.createMemberStatusResult(funcResults, ifExists);
+
+    result.setConfigObject(indexInfo);
 
-    if (xmlEntity != null) {
-      persistClusterConfiguration(result,
-          () -> ((InternalConfigurationPersistenceService) getConfigurationPersistenceService())
-              .deleteXmlEntity(xmlEntity, group));
-    }
     return result;
   }
+
+  @Override
+  public void updateClusterConfig(String group, CacheConfig config, Object element) {
+    RegionConfig.Index indexFromCommand = (RegionConfig.Index) element;
+    String indexName = indexFromCommand.getName();
+
+    String regionName = indexFromCommand.getFromClause();
+    if (regionName != null) {
+      RegionConfig regionConfig = config.findRegionConfiguration(regionName);
+      if (regionConfig == null) {
+        String errorMessage = "Region " + regionName + " not found";
+        if (!ConfigurationPersistenceService.CLUSTER_CONFIG.equals(group)) {
+          errorMessage += " in group " + group;
+        }
+        throw new EntityNotFoundException(errorMessage);
+      }
+
+      if (indexName.isEmpty()) {
+        regionConfig.getIndex().clear();
+      } else {
+        CacheElement.removeElement(regionConfig.getIndex(), indexName);
+      }
+    } else {
+      // Need to search for the index name as region was not specified
+      for (RegionConfig r : config.getRegion()) {
+        CacheElement.removeElement(r.getIndex(), indexName);
+      }
+    }
+  }
+
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DestroyIndexFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DestroyIndexFunction.java
index a7a4c67..3d56eed 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DestroyIndexFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DestroyIndexFunction.java
@@ -19,21 +19,19 @@ import java.util.List;
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.CacheClosedException;
 import org.apache.geode.cache.Region;
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.cache.query.Index;
 import org.apache.geode.cache.query.QueryService;
-import org.apache.geode.internal.cache.execute.InternalFunction;
-import org.apache.geode.internal.cache.xmlcache.CacheXml;
-import org.apache.geode.management.internal.cli.domain.IndexInfo;
+import org.apache.geode.management.cli.CliFunction;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.configuration.domain.XmlEntity;
 
-public class DestroyIndexFunction implements InternalFunction {
+public class DestroyIndexFunction extends CliFunction {
   private static final long serialVersionUID = -868082551095130315L;
 
   @Override
-  public void execute(FunctionContext context) {
-    IndexInfo indexInfo = (IndexInfo) context.getArguments();
+  public CliFunctionResult executeFunction(FunctionContext context) {
+    RegionConfig.Index indexInfo = (RegionConfig.Index) context.getArguments();
     String memberId = null;
 
     CliFunctionResult result;
@@ -41,11 +39,8 @@ public class DestroyIndexFunction implements InternalFunction {
       Cache cache = context.getCache();
       memberId = cache.getDistributedSystem().getDistributedMember().getId();
       QueryService queryService = cache.getQueryService();
-      String indexName = indexInfo.getIndexName();
-      String regionPath = indexInfo.getRegionPath();
-
-      XmlEntity xmlEntity =
-          new XmlEntity(CacheXml.REGION, "name", regionPath, CacheXml.INDEX, "name", indexName);
+      String indexName = indexInfo.getName();
+      String regionPath = indexInfo.getFromClause();
 
       if (regionPath != null && !regionPath.isEmpty()) {
         Region<?, ?> region = cache.getRegion(regionPath);
@@ -53,40 +48,36 @@ public class DestroyIndexFunction implements InternalFunction {
         if (region != null) {
           if (indexName == null || indexName.isEmpty()) {
             queryService.removeIndexes(region);
-            result = new CliFunctionResult(memberId, xmlEntity,
+            result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.OK,
                 "Destroyed all indexes on region " + regionPath);
           } else {
             Index index = queryService.getIndex(region, indexName);
 
             if (index != null) {
               queryService.removeIndex(index);
-              result = new CliFunctionResult(memberId, xmlEntity,
+              result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.OK,
                   "Destroyed index " + indexName + " on region " + regionPath);
-            } else if (indexInfo.isIfExists()) {
-              result = new CliFunctionResult(memberId, true,
-                  "Index " + indexName + " not found - skipped");
             } else {
-              result = new CliFunctionResult(memberId, false,
+              result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.IGNORED,
                   CliStrings.format(CliStrings.DESTROY_INDEX__INDEX__NOT__FOUND, indexName));
             }
           }
         } else {
-          result = new CliFunctionResult(memberId, false,
+          result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.ERROR,
               CliStrings.format(CliStrings.DESTROY_INDEX__REGION__NOT__FOUND, regionPath));
         }
       } else {
         if (indexName == null || indexName.isEmpty()) {
           queryService.removeIndexes();
-          result = new CliFunctionResult(memberId, xmlEntity, "Destroyed all indexes");
+          result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.OK,
+              "Destroyed all indexes");
         } else {
           boolean indexRemoved = removeIndexByName(indexName, queryService);
           if (indexRemoved) {
-            result = new CliFunctionResult(memberId, xmlEntity, "Destroyed index " + indexName);
-          } else if (indexInfo.isIfExists()) {
-            result = new CliFunctionResult(memberId, true,
-                "Index " + indexName + " not found - skipped");
+            result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.OK,
+                "Destroyed index " + indexName);
           } else {
-            result = new CliFunctionResult(memberId, false,
+            result = new CliFunctionResult(memberId, CliFunctionResult.StatusState.IGNORED,
                 CliStrings.format(CliStrings.DESTROY_INDEX__INDEX__NOT__FOUND, indexName));
           }
         }
@@ -97,7 +88,7 @@ public class DestroyIndexFunction implements InternalFunction {
       result = new CliFunctionResult(memberId, e, e.getMessage());
     }
 
-    context.getResultSender().lastResult(result);
+    return result;
   }
 
   /***
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommandDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommandDUnitTest.java
index a7875ba..b8cd1e5 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommandDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommandDUnitTest.java
@@ -64,7 +64,8 @@ public class CreateIndexCommandDUnitTest {
   @Test
   public void regionNotExist() {
     gfsh.executeAndAssertThat("create index --name=myIndex --expression=id --region=/noExist")
-        .statusIsError().containsOutput("ERROR", "Region not found : \"/noExist\"");
+        .statusIsError().tableHasColumnWithExactValuesInAnyOrder("Status", "ERROR")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Region not found : \"/noExist\"");
 
     locator.invoke(() -> {
       InternalConfigurationPersistenceService configurationService =
@@ -92,7 +93,8 @@ public class CreateIndexCommandDUnitTest {
     });
 
     gfsh.executeAndAssertThat("create index --name=myIndex --expression=id --region=regionA")
-        .statusIsSuccess().containsOutput("Index successfully created");
+        .statusIsSuccess().tableHasColumnWithValuesContaining("Status", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Index successfully created");
 
     // after index is created, the cluster config is not udpated with regionA or index
     locator.invoke(() -> {
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommandsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommandsDUnitTest.java
index 1d854da..5af582a 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommandsDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyIndexCommandsDUnitTest.java
@@ -28,8 +28,11 @@ import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.Region;
 import org.apache.geode.cache.RegionFactory;
 import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
 import org.apache.geode.management.internal.cli.domain.Stock;
+import org.apache.geode.test.dunit.IgnoredException;
 import org.apache.geode.test.dunit.rules.ClusterStartupRule;
 import org.apache.geode.test.dunit.rules.MemberVM;
 import org.apache.geode.test.junit.categories.DistributedTest;
@@ -42,6 +45,8 @@ public class DestroyIndexCommandsDUnitTest {
   private static final String REGION_1 = "REGION1";
   private static final String INDEX_1 = "INDEX1";
   private static final String INDEX_2 = "INDEX2";
+  private static final String GROUP_1 = "group1";
+  private static final String GROUP_2 = "group2";
 
   private MemberVM locator;
   private MemberVM server1;
@@ -59,18 +64,25 @@ public class DestroyIndexCommandsDUnitTest {
     props.setProperty(ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER,
         "org.apache.geode.management.internal.cli.domain.Stock");
     locator = rule.startLocatorVM(0);
+
+    props.setProperty("groups", GROUP_1);
     server1 = rule.startServerVM(1, props, locator.getPort());
+    props.setProperty("groups", GROUP_2);
     server2 = rule.startServerVM(2, props, locator.getPort());
 
-    server1.invoke(() -> {
-      createRegionAndIndex();
-    });
+    gfsh.connectAndVerify(locator);
 
-    server2.invoke(() -> {
-      createRegionAndIndex();
-    });
+    gfsh.executeAndAssertThat(String.format("create region --name=%s --type=REPLICATE", REGION_1))
+        .statusIsSuccess();
 
-    gfsh.connectAndVerify(locator);
+    gfsh.executeAndAssertThat(
+        String.format("create index --name=%s --expression=key --region=%s", INDEX_1, REGION_1))
+        .statusIsSuccess();
+    gfsh.executeAndAssertThat(
+        String.format("create index --name=%s --expression=id --region=%s", INDEX_2, REGION_1))
+        .statusIsSuccess();
+
+    assertIndexCount(REGION_1, 2);
   }
 
   private static void createRegionAndIndex() throws Exception {
@@ -86,57 +98,45 @@ public class DestroyIndexCommandsDUnitTest {
   @Test
   public void testDestroyAllIndexesOnRegion() throws Exception {
     gfsh.executeAndAssertThat("destroy index --region=" + REGION_1).statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status",
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "OK", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message",
             "Destroyed all indexes on region REGION1", "Destroyed all indexes on region REGION1");
 
-    server1.invoke(() -> {
-      Cache cache = ClusterStartupRule.getCache();
-      assertThat(cache.getQueryService().getIndexes()).isEmpty();
-    });
-
-    server2.invoke(() -> {
-      Cache cache = ClusterStartupRule.getCache();
-      assertThat(cache.getQueryService().getIndexes()).isEmpty();
-    });
+    assertIndexCount(REGION_1, 0);
 
     // Check idempotency
     gfsh.executeAndAssertThat("destroy index --if-exists --region=" + REGION_1).statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status",
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "OK", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message",
             "Destroyed all indexes on region REGION1", "Destroyed all indexes on region REGION1");
   }
 
   @Test
   public void testDestroyOneIndexOnRegion() throws Exception {
     gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1 + " --region=" + REGION_1)
-        .statusIsSuccess().tableHasColumnWithExactValuesInAnyOrder("Status",
+        .statusIsSuccess().tableHasColumnWithExactValuesInAnyOrder("Message",
             "Destroyed index INDEX1 on region REGION1", "Destroyed index INDEX1 on region REGION1");
-
-    server1.invoke(() -> {
-      Cache cache = ClusterStartupRule.getCache();
-      assertThat(cache.getQueryService().getIndexes().size()).isEqualTo(1);
-    });
-
-    server2.invoke(() -> {
-      Cache cache = ClusterStartupRule.getCache();
-      assertThat(cache.getQueryService().getIndexes().size()).isEqualTo(1);
-    });
+    assertIndexCount(REGION_1, 1);
 
     // Check idempotency
     gfsh.executeAndAssertThat(
         "destroy index --if-exists --name=" + INDEX_1 + " --region=" + REGION_1).statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Index INDEX1 not found - skipped",
-            "Index INDEX1 not found - skipped");
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "IGNORED", "IGNORED")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Index named \"INDEX1\" not found",
+            "Index named \"INDEX1\" not found");
+    assertIndexCount(REGION_1, 1);
 
     // Check error result is correct
     gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1 + " --region=" + REGION_1)
-        .statusIsError().tableHasColumnWithExactValuesInAnyOrder("Status",
-            "ERROR: Index named \"INDEX1\" not found", "ERROR: Index named \"INDEX1\" not found");
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "ERROR", "ERROR").statusIsError()
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Index named \"INDEX1\" not found",
+            "Index named \"INDEX1\" not found");
   }
 
   @Test
   public void testDestroyAllIndexesOnOneMember() throws Exception {
     gfsh.executeAndAssertThat("destroy index --member=server-1").statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Destroyed all indexes");
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Destroyed all indexes");
 
     server1.invoke(() -> {
       Cache cache = ClusterStartupRule.getCache();
@@ -150,14 +150,14 @@ public class DestroyIndexCommandsDUnitTest {
 
     // Check idempotency
     gfsh.executeAndAssertThat("destroy index --if-exists --member=server-1").statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Destroyed all indexes");
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Destroyed all indexes");
   }
 
   @Test
   public void testDestroyOneIndexOnOneMember() throws Exception {
     gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1 + " --member=server-1")
         .statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Destroyed index INDEX1");
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Destroyed index INDEX1");
 
     server1.invoke(() -> {
       Cache cache = ClusterStartupRule.getCache();
@@ -171,20 +171,20 @@ public class DestroyIndexCommandsDUnitTest {
 
     // Check idempotency
     gfsh.executeAndAssertThat("destroy index --if-exists --name=" + INDEX_1 + " --member=server-1")
-        .statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Index INDEX1 not found - skipped");
+        .statusIsSuccess().tableHasColumnWithExactValuesInAnyOrder("Status", "IGNORED")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Index named \"INDEX1\" not found");
 
     // Check error result is correct
     gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1 + " --member=server-1")
-        .statusIsError().tableHasColumnWithExactValuesInAnyOrder("Status",
-            "ERROR: Index named \"INDEX1\" not found");
+        .statusIsError().tableHasColumnWithExactValuesInAnyOrder("Status", "ERROR")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Index named \"INDEX1\" not found");
   }
 
   @Test
-  public void testVariableResultDestroyOneIndex() throws Exception {
+  public void testPartialSuccessResultDestroyOneIndex() throws Exception {
     gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1 + " --member=server-1")
-        .statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Destroyed index INDEX1");
+        .statusIsSuccess().tableHasColumnWithExactValuesInAnyOrder("Status", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Destroyed index INDEX1");
 
     server1.invoke(() -> {
       Cache cache = ClusterStartupRule.getCache();
@@ -198,8 +198,82 @@ public class DestroyIndexCommandsDUnitTest {
 
     // Check error on partial failure
     gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1).statusIsSuccess()
-        .tableHasColumnWithExactValuesInAnyOrder("Status", "Destroyed index INDEX1",
-            "ERROR: Index named \"INDEX1\" not found");
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "ERROR", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Index named \"INDEX1\" not found",
+            "Destroyed index INDEX1");
+
+    assertIndexCount(REGION_1, 1);
+  }
 
+  @Test
+  public void destroyIndexOnOneGroupWithoutAssociatedClusterConfig() {
+    gfsh.executeAndAssertThat("destroy index --name=" + INDEX_1 + " --group=" + GROUP_1)
+        .statusIsSuccess().tableHasColumnWithValuesContaining("Member", "server-1")
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Destroyed index INDEX1");
+
+    gfsh.executeAndAssertThat("destroy index --name=" + INDEX_2 + " --group=" + GROUP_2)
+        .statusIsSuccess().tableHasColumnWithValuesContaining("Member", "server-2")
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "OK")
+        .tableHasColumnWithExactValuesInAnyOrder("Message", "Destroyed index INDEX2");
+
+    // The index count on each server and the cluster config will now have diverged because the
+    // index+region were not originally defined per group but at the cluster level.
+    server1.invoke(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      assertThat(cache.getQueryService().getIndexes().size()).isEqualTo(1);
+    });
+
+    server2.invoke(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      assertThat(cache.getQueryService().getIndexes().size()).isEqualTo(1);
+    });
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService svc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      RegionConfig regionConfig = svc.getCacheConfig("cluster").findRegionConfiguration(REGION_1);
+      assertThat(regionConfig.getIndex().size()).isEqualTo(2);
+    });
+  }
+
+  @Test
+  public void destroyIndexOnRegionNotInClusterConfig() {
+    IgnoredException.addIgnoredException("failed to update cluster config for cluster");
+    IgnoredException.addIgnoredException(
+        "org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException");
+
+    server1.invoke(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      RegionFactory factory = cache.createRegionFactory(RegionShortcut.REPLICATE);
+      factory.create("REGION3");
+      cache.getQueryService().createIndex("INDEX3", "key", "/REGION3");
+    });
+
+    gfsh.executeAndAssertThat("destroy index --name=INDEX3" + " --region=REGION3").statusIsSuccess()
+        .tableHasColumnWithExactValuesInAnyOrder("Status", "OK", "ERROR")
+        .tableHasColumnWithExactValuesInAnyOrder("Message",
+            "Destroyed index INDEX3 on region REGION3", "Region \"REGION3\" not found");
+
+    assertIndexCount(REGION_1, 2);
+  }
+
+  private void assertIndexCount(String region, int indexCount) {
+    server1.invoke(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      assertThat(cache.getQueryService().getIndexes().size()).isEqualTo(indexCount);
+    });
+
+    server2.invoke(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      assertThat(cache.getQueryService().getIndexes().size()).isEqualTo(indexCount);
+    });
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService svc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      RegionConfig regionConfig = svc.getCacheConfig("cluster").findRegionConfiguration(region);
+      assertThat(regionConfig.getIndex().size()).isEqualTo(indexCount);
+    });
   }
 }
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsIntegrationTest.java
index 158ef67..2774ae0 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsIntegrationTest.java
@@ -18,6 +18,8 @@ import static org.apache.geode.distributed.ConfigurationProperties.GROUPS;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 import org.junit.After;
 import org.junit.Before;
@@ -37,6 +39,7 @@ import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.internal.cli.domain.Stock;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
 import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
 import org.apache.geode.test.junit.categories.GfshTest;
 import org.apache.geode.test.junit.categories.IntegrationTest;
@@ -207,7 +210,7 @@ public class IndexCommandsIntegrationTest {
     csb.addOption(CliStrings.DESTROY_INDEX__REGION, "IncorrectRegion");
     CommandResult result = gfsh.executeCommand(csb.toString());
     assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
-    assertThat(gfsh.getGfshOutput()).contains("ERROR: Region \"IncorrectRegion\" not found");
+    assertThat(gfsh.getGfshOutput()).contains("ERROR", "Region \"IncorrectRegion\" not found");
   }
 
   @Test
@@ -286,8 +289,13 @@ public class IndexCommandsIntegrationTest {
     csb.addOption(CliStrings.IFEXISTS);
     gfsh.executeAndAssertThat(csb.toString()).statusIsSuccess()
         .containsOutput("Destroyed index " + indexName);
-    gfsh.executeAndAssertThat(csb.toString()).statusIsSuccess()
-        .containsOutput("Index " + indexName + " not found - skipped");
+    CommandResult result = gfsh.executeCommand(csb.toString());
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+
+    Map<String, List<String>> table =
+        result.getMapFromTableContent(ResultModel.MEMBER_STATUS_SECTION);
+    assertThat(table.get("Status")).containsExactly("IGNORED");
+    assertThat(table.get("Message")).containsExactly("Index named \"" + indexName + "\" not found");
   }
 
   private void createSimpleIndexA() throws Exception {

-- 
To stop receiving notification emails like this one, please contact
jensdeppe@apache.org.