Browse Source

Autoscaling delete policy by simple pattern (#64739)

Added the capability to delete autoscaling policies by pattern, allowing
to for instance do:
```
DELETE _autoscaling/policy/*
```
to delete all autoscaling policies. If a wildcard is involved, no
matches are required.
Henning Andersen 5 years ago
parent
commit
be9725245d

+ 17 - 0
docs/reference/autoscaling/apis/delete-autoscaling-policy.asciidoc

@@ -61,3 +61,20 @@ The API returns the following result:
   "acknowledged": true
 }
 --------------------------------------------------
+
+This example deletes all autoscaling policies.
+
+[source,console]
+--------------------------------------------------
+DELETE /_autoscaling/policy/*
+--------------------------------------------------
+// TEST
+
+The API returns the following result:
+
+[source,console-result]
+--------------------------------------------------
+{
+  "acknowledged": true
+}
+--------------------------------------------------

+ 61 - 0
x-pack/plugin/autoscaling/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/autoscaling/delete_autoscaling_policy.yml

@@ -31,3 +31,64 @@
       catch: /autoscaling policy with name \[does_not_exist\] does not exist/
       autoscaling.delete_autoscaling_policy:
         name: does_not_exist
+
+---
+"Test delete all non-existing autoscaling policies":
+  - do:
+      autoscaling.delete_autoscaling_policy:
+        name: "*"
+
+---
+"Test delete all existing autoscaling policies":
+  - do:
+      autoscaling.put_autoscaling_policy:
+        name: my_autoscaling_policy_1
+        body:
+          roles: []
+
+  - do:
+      autoscaling.put_autoscaling_policy:
+        name: my_autoscaling_policy_2
+        body:
+          roles: []
+
+  - do:
+      autoscaling.delete_autoscaling_policy:
+        name: "*"
+
+  - do:
+      catch: missing
+      autoscaling.get_autoscaling_policy:
+        name: my_autoscaling_policy_1
+
+  - do:
+      catch: missing
+      autoscaling.get_autoscaling_policy:
+        name: my_autoscaling_policy_2
+
+---
+"Test delete autoscaling policies by wildcard":
+  - do:
+      autoscaling.put_autoscaling_policy:
+        name: my_autoscaling_policy_delete
+        body:
+          roles: []
+
+  - do:
+      autoscaling.put_autoscaling_policy:
+        name: my_autoscaling_policy_keep
+        body:
+          roles: []
+
+  - do:
+      autoscaling.delete_autoscaling_policy:
+        name: "my_autoscaling_policy_delete*"
+
+  - do:
+      catch: missing
+      autoscaling.get_autoscaling_policy:
+        name: my_autoscaling_policy_delete
+
+  - do:
+      autoscaling.get_autoscaling_policy:
+        name: my_autoscaling_policy_keep

+ 7 - 1
x-pack/plugin/autoscaling/src/internalClusterTest/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyActionIT.java

@@ -30,7 +30,8 @@ public class TransportDeleteAutoscalingPolicyActionIT extends AutoscalingIntegTe
         );
         assertAcked(client().execute(PutAutoscalingPolicyAction.INSTANCE, putRequest).actionGet());
         // we trust that the policy is in the cluster state since we have tests for putting policies
-        final DeleteAutoscalingPolicyAction.Request deleteRequest = new DeleteAutoscalingPolicyAction.Request(policy.name());
+        String deleteName = randomFrom("*", policy.name(), policy.name().substring(0, between(0, policy.name().length())) + "*");
+        final DeleteAutoscalingPolicyAction.Request deleteRequest = new DeleteAutoscalingPolicyAction.Request(deleteName);
         assertAcked(client().execute(DeleteAutoscalingPolicyAction.INSTANCE, deleteRequest).actionGet());
         // now verify that the policy is not in the cluster state
         final ClusterState state = client().admin().cluster().prepareState().get().getState();
@@ -56,4 +57,9 @@ public class TransportDeleteAutoscalingPolicyActionIT extends AutoscalingIntegTe
         assertThat(e.getMessage(), containsString("autoscaling policy with name [" + name + "] does not exist"));
     }
 
+    public void testDeleteNonExistentPolicyByWildcard() {
+        final String name = randomFrom("*", randomAlphaOfLength(8) + "*");
+        final DeleteAutoscalingPolicyAction.Request deleteRequest = new DeleteAutoscalingPolicyAction.Request(name);
+        assertAcked(client().execute(DeleteAutoscalingPolicyAction.INSTANCE, deleteRequest).actionGet());
+    }
 }

+ 16 - 0
x-pack/plugin/autoscaling/src/internalClusterTest/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionIT.java

@@ -8,6 +8,7 @@ package org.elasticsearch.xpack.autoscaling.action;
 
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.xpack.autoscaling.AutoscalingIntegTestCase;
 import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata;
 import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase;
@@ -15,7 +16,10 @@ import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy;
 
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.mutateAutoscalingDeciders;
+import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomAutoscalingDeciders;
 import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomAutoscalingPolicy;
+import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomRoles;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasKey;
 import static org.hamcrest.Matchers.sameInstance;
@@ -57,6 +61,18 @@ public class TransportPutAutoscalingPolicyActionIT extends AutoscalingIntegTestC
         );
     }
 
+    public void testPutPolicyIllegalName() {
+        IllegalArgumentException exception = expectThrows(
+            IllegalArgumentException.class,
+            () -> putAutoscalingPolicy(new AutoscalingPolicy(randomAlphaOfLength(8) + "*", randomRoles(), randomAutoscalingDeciders()))
+        );
+
+        assertThat(
+            exception.getMessage(),
+            containsString("name must not contain the following characters " + Strings.INVALID_FILENAME_CHARS)
+        );
+    }
+
     private AutoscalingPolicy putRandomAutoscalingPolicy() {
         final AutoscalingPolicy policy = randomAutoscalingPolicy();
         putAutoscalingPolicy(policy);

+ 12 - 3
x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/PutAutoscalingPolicyAction.java

@@ -8,9 +8,11 @@ package org.elasticsearch.xpack.autoscaling.action;
 
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.ActionType;
+import org.elasticsearch.action.ValidateActions;
 import org.elasticsearch.action.support.master.AcknowledgedRequest;
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.util.set.Sets;
@@ -136,18 +138,25 @@ public class PutAutoscalingPolicyAction extends ActionType<AcknowledgedResponse>
 
         @Override
         public ActionRequestValidationException validate() {
+            ActionRequestValidationException exception = null;
             if (roles != null) {
                 List<String> errors = roles.stream()
                     .filter(Predicate.not(DiscoveryNode.getPossibleRoleNames()::contains))
                     .collect(Collectors.toList());
                 if (errors.isEmpty() == false) {
-                    ActionRequestValidationException exception = new ActionRequestValidationException();
+                    exception = new ActionRequestValidationException();
                     exception.addValidationErrors(errors);
-                    return exception;
                 }
             }
 
-            return null;
+            if (Strings.validFileName(name) == false) {
+                exception = ValidateActions.addValidationError(
+                    "name must not contain the following characters " + Strings.INVALID_FILENAME_CHARS,
+                    exception
+                );
+            }
+
+            return exception;
         }
 
         @Override

+ 12 - 4
x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyAction.java

@@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.regex.Regex;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
@@ -83,13 +84,20 @@ public class TransportDeleteAutoscalingPolicyAction extends AcknowledgedTranspor
             // we will reject the request below when we try to look up the policy by name
             currentMetadata = AutoscalingMetadata.EMPTY;
         }
-        if (currentMetadata.policies().containsKey(name) == false) {
+        boolean wildcard = Regex.isSimpleMatchPattern(name);
+        if (wildcard == false && currentMetadata.policies().containsKey(name) == false) {
             throw new ResourceNotFoundException("autoscaling policy with name [" + name + "] does not exist");
         }
+
         final SortedMap<String, AutoscalingPolicyMetadata> newPolicies = new TreeMap<>(currentMetadata.policies());
-        final AutoscalingPolicyMetadata policy = newPolicies.remove(name);
-        assert policy != null : name;
-        logger.info("deleting autoscaling policy [{}]", name);
+        if (wildcard) {
+            newPolicies.keySet().removeIf(key -> Regex.simpleMatch(name, key));
+            logger.info("deleting [{}] autoscaling policies", currentMetadata.policies().size() - newPolicies.size());
+        } else {
+            final AutoscalingPolicyMetadata policy = newPolicies.remove(name);
+            assert policy != null : name;
+            logger.info("deleting autoscaling policy [{}]", name);
+        }
         final AutoscalingMetadata newMetadata = new AutoscalingMetadata(newPolicies);
         builder.metadata(Metadata.builder(currentState.getMetadata()).putCustom(AutoscalingMetadata.NAME, newMetadata).build());
         return builder.build();

+ 35 - 0
x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyActionTests.java

@@ -17,6 +17,7 @@ import org.elasticsearch.cluster.coordination.NoMasterBlockService;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.common.regex.Regex;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata;
@@ -103,6 +104,40 @@ public class TransportDeleteAutoscalingPolicyActionTests extends AutoscalingTest
         }
     }
 
+    public void testDeletePolicyByWildcard() {
+        final ClusterState currentState;
+        {
+            final ClusterState.Builder builder = ClusterState.builder(new ClusterName(randomAlphaOfLength(8)));
+            builder.metadata(
+                Metadata.builder().putCustom(AutoscalingMetadata.NAME, randomAutoscalingMetadataOfPolicyCount(randomIntBetween(1, 8)))
+            );
+            currentState = builder.build();
+        }
+        final AutoscalingMetadata currentMetadata = currentState.metadata().custom(AutoscalingMetadata.NAME);
+        final String policyName = randomFrom(currentMetadata.policies().keySet());
+        final String deleteName = randomFrom(policyName.substring(0, between(0, policyName.length()))) + "*";
+        final Logger mockLogger = mock(Logger.class);
+        final ClusterState state = TransportDeleteAutoscalingPolicyAction.deleteAutoscalingPolicy(currentState, deleteName, mockLogger);
+
+        // ensure the policy is deleted from the cluster state
+        final AutoscalingMetadata metadata = state.metadata().custom(AutoscalingMetadata.NAME);
+        assertNotNull(metadata);
+        assertThat(metadata.policies(), not(hasKey(policyName)));
+
+        verify(mockLogger).info("deleting [{}] autoscaling policies", currentMetadata.policies().size() - metadata.policies().size());
+        verifyNoMoreInteractions(mockLogger);
+
+        // ensure that the right policies were preserved
+        for (final Map.Entry<String, AutoscalingPolicyMetadata> entry : currentMetadata.policies().entrySet()) {
+            if (Regex.simpleMatch(deleteName, entry.getKey())) {
+                assertFalse(metadata.policies().containsKey(entry.getKey()));
+            } else {
+                assertThat(metadata.policies(), hasKey(entry.getKey()));
+                assertThat(metadata.policies().get(entry.getKey()).policy(), equalTo(entry.getValue().policy()));
+            }
+        }
+    }
+
     public void testDeleteNonExistentPolicy() {
         final ClusterState currentState;
         {