From f670ae0bc823976fea6e3fa2f619afd09faca39d Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 1 Apr 2020 07:35:45 -0400 Subject: [PATCH] Introduce autoscaling policies (#54473) This commit is the first in a series of commits that introduces autoscaling policies, and APIs for working with them. For now, we introduce the basic infrastructure, and a single API for putting an autoscaling policy. We will follow in rapid succession with APIs for getting, and deleting autoscaling policies. --- .../apis/autoscaling-apis.asciidoc | 2 + .../apis/put-autoscaling-policy.asciidoc | 67 +++++++ x-pack/plugin/autoscaling/build.gradle | 16 ++ .../autoscaling/put_autoscaling_policy.yml | 21 +++ .../xpack/autoscaling/Autoscaling.java | 65 ++++++- .../autoscaling/AutoscalingMetadata.java | 169 ++++++++++++++++++ .../action/GetAutoscalingDecisionAction.java | 2 +- .../action/PutAutoscalingPolicyAction.java | 90 ++++++++++ .../TransportPutAutoscalingPolicyAction.java | 122 +++++++++++++ .../decision/AlwaysAutoscalingDecider.java | 74 ++++++++ .../decision/AutoscalingDecider.java | 31 ++++ .../{ => decision}/AutoscalingDecision.java | 2 +- .../AutoscalingDecisionType.java | 2 +- .../{ => decision}/AutoscalingDecisions.java | 4 +- .../autoscaling/policy/AutoscalingPolicy.java | 121 +++++++++++++ .../policy/AutoscalingPolicyMetadata.java | 85 +++++++++ .../rest/RestPutAutoscalingPolicyHandler.java | 44 +++++ .../autoscaling/AutoscalingIntegTestCase.java | 46 +++++ ...ingMetadataDiffableSerializationTests.java | 82 +++++++++ .../autoscaling/AutoscalingTestCase.java | 74 +++++++- .../autoscaling/LocalStateAutoscaling.java | 27 +++ ...TransportPutAutoscalingPolicyActionIT.java | 66 +++++++ ...nsportPutAutoscalingPolicyActionTests.java | 164 +++++++++++++++++ .../AutoscalingDecisionTests.java | 3 +- ...alingDecisionTypeWireSerializingTests.java | 2 +- ...toscalingDecisionWireSerializingTests.java | 3 +- .../AutoscalingDecisionsTests.java | 4 +- ...oscalingDecisionsWireSerializingTests.java | 3 +- ...icyMetadataDiffableSerializationTests.java | 66 +++++++ .../AutoscalingPolicySerializingTests.java | 54 ++++++ .../core/LocalStateCompositeXPackPlugin.java | 2 +- .../autoscaling.put_autoscaling_policy.json | 28 +++ 32 files changed, 1521 insertions(+), 20 deletions(-) create mode 100644 docs/reference/autoscaling/apis/put-autoscaling-policy.asciidoc create mode 100644 x-pack/plugin/autoscaling/qa/rest/src/test/resources/rest-api-spec/test/autoscaling/put_autoscaling_policy.yml create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadata.java create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/PutAutoscalingPolicyAction.java create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AlwaysAutoscalingDecider.java create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecider.java rename x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecision.java (97%) rename x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisionType.java (97%) rename x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisions.java (93%) create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicy.java create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadata.java create mode 100644 x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/rest/RestPutAutoscalingPolicyHandler.java create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingIntegTestCase.java create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadataDiffableSerializationTests.java create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/LocalStateAutoscaling.java create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionIT.java create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionTests.java rename x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisionTests.java (90%) rename x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisionTypeWireSerializingTests.java (97%) rename x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisionWireSerializingTests.java (86%) rename x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisionsTests.java (92%) rename x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/{ => decision}/AutoscalingDecisionsWireSerializingTests.java (86%) create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadataDiffableSerializationTests.java create mode 100644 x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicySerializingTests.java create mode 100644 x-pack/plugin/src/test/resources/rest-api-spec/api/autoscaling.put_autoscaling_policy.json diff --git a/docs/reference/autoscaling/apis/autoscaling-apis.asciidoc b/docs/reference/autoscaling/apis/autoscaling-apis.asciidoc index 6a794362e4a..078c5683059 100644 --- a/docs/reference/autoscaling/apis/autoscaling-apis.asciidoc +++ b/docs/reference/autoscaling/apis/autoscaling-apis.asciidoc @@ -10,6 +10,8 @@ You can use the following APIs to perform autoscaling operations. === Top-Level * <> +* <> // top-level include::get-autoscaling-decision.asciidoc[] +include::put-autoscaling-policy.asciidoc[] diff --git a/docs/reference/autoscaling/apis/put-autoscaling-policy.asciidoc b/docs/reference/autoscaling/apis/put-autoscaling-policy.asciidoc new file mode 100644 index 00000000000..5a453c2e34b --- /dev/null +++ b/docs/reference/autoscaling/apis/put-autoscaling-policy.asciidoc @@ -0,0 +1,67 @@ +[role="xpack"] +[testenv="platinum"] +[[autoscaling-put-autoscaling-policy]] +=== Put autoscaling policy API +++++ +Put autoscaling policy +++++ + +Put autoscaling policy. + +[[autoscaling-put-autoscaling-policy-request]] +==== {api-request-title} + +[source,console] +-------------------------------------------------- +PUT /_autoscaling/policy/ +{ + "policy": { + "deciders": { + "always": { + } + } + } +} +-------------------------------------------------- +// TEST[s//name/] + +[[autoscaling-put-autoscaling-policy-prereqs]] +==== {api-prereq-title} + +* If the {es} {security-features} are enabled, you must have +`manage_autoscaling` cluster privileges. For more information, see +<>. + +[[autoscaling-put-autoscaling-policy-desc]] +==== {api-description-title} + +This API puts an autoscaling policy with the provided name. + +[[autoscaling-put-autoscaling-policy-examples]] +==== {api-examples-title} + +This example puts an autoscaling policy named `hot` using the always +autoscaling decider. + +[source,console] +-------------------------------------------------- +PUT /_autoscaling/policy/hot +{ + "policy": { + "deciders": { + "always": { + } + } + } +} +-------------------------------------------------- +// TEST + +The API returns the following result: + +[source,console-result] +-------------------------------------------------- +{ + "acknowledged": true +} +-------------------------------------------------- diff --git a/x-pack/plugin/autoscaling/build.gradle b/x-pack/plugin/autoscaling/build.gradle index 709b988a2ab..ea76da209c7 100644 --- a/x-pack/plugin/autoscaling/build.gradle +++ b/x-pack/plugin/autoscaling/build.gradle @@ -14,6 +14,16 @@ archivesBaseName = 'x-pack-autoscaling' integTest.enabled = false +task internalClusterTest(type: Test) { + description = 'Java fantasy integration tests' + mustRunAfter test + + include '**/*IT.class' + systemProperty 'es.set.netty.runtime.available.processors', 'false' +} + +check.dependsOn internalClusterTest + dependencies { compileOnly project(path: xpackModule('core'), configuration: 'default') testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') @@ -27,3 +37,9 @@ gradle.projectsEvaluated { .findAll { it.path.startsWith(project.path + ":qa") } .each { check.dependsOn it.check } } + +testingConventions.naming { + IT { + baseClass "org.elasticsearch.xpack.autoscaling.AutoscalingIntegTestCase" + } +} diff --git a/x-pack/plugin/autoscaling/qa/rest/src/test/resources/rest-api-spec/test/autoscaling/put_autoscaling_policy.yml b/x-pack/plugin/autoscaling/qa/rest/src/test/resources/rest-api-spec/test/autoscaling/put_autoscaling_policy.yml new file mode 100644 index 00000000000..93d5377be36 --- /dev/null +++ b/x-pack/plugin/autoscaling/qa/rest/src/test/resources/rest-api-spec/test/autoscaling/put_autoscaling_policy.yml @@ -0,0 +1,21 @@ +--- +"Test put autoscaling decision": + - do: + autoscaling.put_autoscaling_policy: + name: hot + body: + policy: + deciders: + always: {} + + - match: { "acknowledged": true } + + - do: + catch: bad_request + autoscaling.put_autoscaling_policy: + name: hot + body: + policy: + deciders: + does_not_exist: {} + diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java index 2053fc23124..e68a45de833 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java @@ -9,21 +9,34 @@ package org.elasticsearch.xpack.autoscaling; import org.elasticsearch.Build; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingDecisionAction; +import org.elasticsearch.xpack.autoscaling.action.PutAutoscalingPolicyAction; import org.elasticsearch.xpack.autoscaling.action.TransportGetAutoscalingDecisionAction; +import org.elasticsearch.xpack.autoscaling.action.TransportPutAutoscalingPolicyAction; +import org.elasticsearch.xpack.autoscaling.decision.AlwaysAutoscalingDecider; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecider; import org.elasticsearch.xpack.autoscaling.rest.RestGetAutoscalingDecisionHandler; +import org.elasticsearch.xpack.autoscaling.rest.RestPutAutoscalingPolicyHandler; +import org.elasticsearch.xpack.core.XPackPlugin; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -79,11 +92,18 @@ public class Autoscaling extends Plugin implements ActionPlugin { } } + boolean isSnapshot() { + return Build.CURRENT.isSnapshot(); + } + @Override public List> getActions() { if (enabled) { - return Collections.singletonList( - new ActionHandler<>(GetAutoscalingDecisionAction.INSTANCE, TransportGetAutoscalingDecisionAction.class) + return Collections.unmodifiableList( + Arrays.asList( + new ActionHandler<>(GetAutoscalingDecisionAction.INSTANCE, TransportGetAutoscalingDecisionAction.class), + new ActionHandler<>(PutAutoscalingPolicyAction.INSTANCE, TransportPutAutoscalingPolicyAction.class) + ) ); } else { return Collections.emptyList(); @@ -101,14 +121,49 @@ public class Autoscaling extends Plugin implements ActionPlugin { final Supplier nodesInCluster ) { if (enabled) { - return Collections.singletonList(new RestGetAutoscalingDecisionHandler()); + return Collections.unmodifiableList( + Arrays.asList(new RestGetAutoscalingDecisionHandler(), new RestPutAutoscalingPolicyHandler()) + ); } else { return Collections.emptyList(); } } - boolean isSnapshot() { - return Build.CURRENT.isSnapshot(); + @Override + public List getNamedWriteables() { + return Collections.unmodifiableList( + Arrays.asList( + new NamedWriteableRegistry.Entry(Metadata.Custom.class, AutoscalingMetadata.NAME, AutoscalingMetadata::new), + new NamedWriteableRegistry.Entry( + NamedDiff.class, + AutoscalingMetadata.NAME, + AutoscalingMetadata.AutoscalingMetadataDiff::new + ), + new NamedWriteableRegistry.Entry(AutoscalingDecider.class, AlwaysAutoscalingDecider.NAME, AlwaysAutoscalingDecider::new) + ) + ); + } + + @Override + public List getNamedXContent() { + return Collections.unmodifiableList( + Arrays.asList( + new NamedXContentRegistry.Entry( + Metadata.Custom.class, + new ParseField(AutoscalingMetadata.NAME), + AutoscalingMetadata::parse + ), + new NamedXContentRegistry.Entry( + AutoscalingDecider.class, + new ParseField(AlwaysAutoscalingDecider.NAME), + AlwaysAutoscalingDecider::parse + ) + ) + ); + } + + protected XPackLicenseState getLicenseState() { + return XPackPlugin.getSharedLicenseState(); } } diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadata.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadata.java new file mode 100644 index 00000000000..37681db9a9a --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadata.java @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.cluster.DiffableUtils; +import org.elasticsearch.cluster.NamedDiff; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicyMetadata; + +import java.io.IOException; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class AutoscalingMetadata implements Metadata.Custom { + + public static final String NAME = "autoscaling"; + + public static final AutoscalingMetadata EMPTY = new AutoscalingMetadata(Collections.emptySortedMap()); + + private static final ParseField POLICIES_FIELD = new ParseField("policies"); + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + NAME, + c -> new AutoscalingMetadata( + new TreeMap<>( + ((List) c[0]).stream().collect(Collectors.toMap(p -> p.policy().name(), Function.identity())) + ) + ) + ); + + static { + PARSER.declareNamedObjects( + ConstructingObjectParser.constructorArg(), + (p, c, n) -> AutoscalingPolicyMetadata.parse(p, n), + POLICIES_FIELD + ); + } + + public static AutoscalingMetadata parse(final XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final SortedMap policies; + + public SortedMap policies() { + return policies; + } + + public AutoscalingMetadata(final SortedMap policies) { + this.policies = policies; + } + + public AutoscalingMetadata(final StreamInput in) throws IOException { + final int size = in.readVInt(); + final SortedMap policies = new TreeMap<>(); + for (int i = 0; i < size; i++) { + final AutoscalingPolicyMetadata policyMetadata = new AutoscalingPolicyMetadata(in); + policies.put(policyMetadata.policy().name(), policyMetadata); + } + this.policies = policies; + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeVInt(policies.size()); + for (final Map.Entry policy : policies.entrySet()) { + policy.getValue().writeTo(out); + } + } + + @Override + public EnumSet context() { + return Metadata.ALL_CONTEXTS; + } + + @Override + public Diff diff(final Metadata.Custom previousState) { + return new AutoscalingMetadataDiff((AutoscalingMetadata) previousState, this); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public Version getMinimalSupportedVersion() { + return Version.V_7_8_0; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field(POLICIES_FIELD.getPreferredName(), policies); + return builder; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final AutoscalingMetadata metadata = (AutoscalingMetadata) o; + return policies.equals(metadata.policies); + } + + @Override + public int hashCode() { + return Objects.hash(policies); + } + + public static class AutoscalingMetadataDiff implements NamedDiff { + + final Diff> policies; + + public AutoscalingMetadataDiff(final AutoscalingMetadata before, final AutoscalingMetadata after) { + this.policies = DiffableUtils.diff(before.policies, after.policies, DiffableUtils.getStringKeySerializer()); + } + + public AutoscalingMetadataDiff(final StreamInput in) throws IOException { + this.policies = DiffableUtils.readJdkMapDiff( + in, + DiffableUtils.getStringKeySerializer(), + AutoscalingPolicyMetadata::new, + AutoscalingMetadataDiff::readFrom + ); + } + + @Override + public Metadata.Custom apply(final Metadata.Custom part) { + return new AutoscalingMetadata(new TreeMap<>(policies.apply(((AutoscalingMetadata) part).policies))); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + policies.writeTo(out); + } + + static Diff readFrom(final StreamInput in) throws IOException { + return AbstractDiffable.readDiffFrom(AutoscalingPolicyMetadata::new, in); + } + + } + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/GetAutoscalingDecisionAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/GetAutoscalingDecisionAction.java index 8e97eb0d3f6..ff21ae35983 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/GetAutoscalingDecisionAction.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/GetAutoscalingDecisionAction.java @@ -14,7 +14,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.xpack.autoscaling.AutoscalingDecisions; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisions; import java.io.IOException; import java.util.Map; diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/PutAutoscalingPolicyAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/PutAutoscalingPolicyAction.java new file mode 100644 index 00000000000..49539bb1967 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/PutAutoscalingPolicyAction.java @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.action; + +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.ActionType; +import org.elasticsearch.action.support.master.AcknowledgedRequest; +import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy; + +import java.io.IOException; + +public class PutAutoscalingPolicyAction extends ActionType { + + public static final PutAutoscalingPolicyAction INSTANCE = new PutAutoscalingPolicyAction(); + public static final String NAME = "cluster:admin/autoscaling/put_autoscaling_policy"; + + private PutAutoscalingPolicyAction() { + super(NAME, AcknowledgedResponse::new); + } + + public static class Request extends AcknowledgedRequest implements ToXContentObject { + + static final ParseField POLICY_FIELD = new ParseField("policy"); + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "put_autoscaling_policy_request", + a -> new Request((AutoscalingPolicy) a[0]) + ); + + static { + PARSER.declareObject(ConstructingObjectParser.constructorArg(), AutoscalingPolicy::parse, POLICY_FIELD); + } + + public static Request parse(final XContentParser parser, final String name) { + return PARSER.apply(parser, name); + } + + private final AutoscalingPolicy policy; + + public AutoscalingPolicy policy() { + return policy; + } + + public Request(final AutoscalingPolicy policy) { + this.policy = policy; + } + + public Request(final StreamInput in) throws IOException { + super(in); + policy = new AutoscalingPolicy(in); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + super.writeTo(out); + policy.writeTo(out); + } + + @Override + public ActionRequestValidationException validate() { + // TODO: validate that the policy deciders are non-empty + return null; + } + + @Override + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(); + { + builder.field(POLICY_FIELD.getPreferredName(), policy); + } + builder.endObject(); + return builder; + } + + } + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java new file mode 100644 index 00000000000..f1d6c24eff4 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.action; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.action.support.master.TransportMasterNodeAction; +import org.elasticsearch.cluster.AckedClusterStateUpdateTask; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +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.io.stream.StreamInput; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicyMetadata; + +import java.io.IOException; +import java.util.SortedMap; +import java.util.TreeMap; + +public class TransportPutAutoscalingPolicyAction extends TransportMasterNodeAction< + PutAutoscalingPolicyAction.Request, + AcknowledgedResponse> { + + private static final Logger logger = LogManager.getLogger(TransportPutAutoscalingPolicyAction.class); + + @Inject + public TransportPutAutoscalingPolicyAction( + final TransportService transportService, + final ClusterService clusterService, + final ThreadPool threadPool, + final ActionFilters actionFilters, + final IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + PutAutoscalingPolicyAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + PutAutoscalingPolicyAction.Request::new, + indexNameExpressionResolver + ); + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected AcknowledgedResponse read(final StreamInput in) throws IOException { + return new AcknowledgedResponse(in); + } + + @Override + protected void masterOperation( + final PutAutoscalingPolicyAction.Request request, + final ClusterState state, + ActionListener listener + ) { + clusterService.submitStateUpdateTask( + "put-autoscaling-policy", + new AckedClusterStateUpdateTask(request, listener) { + + @Override + protected AcknowledgedResponse newResponse(final boolean acknowledged) { + return new AcknowledgedResponse(acknowledged); + } + + @Override + public ClusterState execute(final ClusterState currentState) { + return putAutoscalingPolicy(currentState, request.policy(), logger); + } + + } + ); + } + + @Override + protected ClusterBlockException checkBlock(final PutAutoscalingPolicyAction.Request request, final ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } + + static ClusterState putAutoscalingPolicy(final ClusterState currentState, final AutoscalingPolicy policy, final Logger logger) { + final ClusterState.Builder builder = ClusterState.builder(currentState); + final AutoscalingMetadata currentMetadata; + if (currentState.metadata().custom(AutoscalingMetadata.NAME) != null) { + currentMetadata = currentState.metadata().custom(AutoscalingMetadata.NAME); + } else { + currentMetadata = AutoscalingMetadata.EMPTY; + } + final SortedMap newPolicies = new TreeMap<>(currentMetadata.policies()); + final AutoscalingPolicyMetadata newPolicyMetadata = new AutoscalingPolicyMetadata(policy); + final AutoscalingPolicyMetadata oldPolicyMetadata = newPolicies.put(policy.name(), newPolicyMetadata); + if (oldPolicyMetadata == null) { + logger.info("adding autoscaling policy [{}]", policy.name()); + } else if (oldPolicyMetadata.equals(newPolicyMetadata)) { + logger.info("skipping updating autoscaling policy [{}] due to no change in policy", policy.name()); + return currentState; + } else { + logger.info("updating autoscaling policy [{}]", policy.name()); + } + final AutoscalingMetadata newMetadata = new AutoscalingMetadata(newPolicies); + builder.metadata(Metadata.builder(currentState.getMetadata()).putCustom(AutoscalingMetadata.NAME, newMetadata).build()); + return builder.build(); + } + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AlwaysAutoscalingDecider.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AlwaysAutoscalingDecider.java new file mode 100644 index 00000000000..ef9e1ac0f0a --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AlwaysAutoscalingDecider.java @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.decision; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class AlwaysAutoscalingDecider implements AutoscalingDecider { + + public static final String NAME = "always"; + + private static final ObjectParser PARSER = new ObjectParser<>(NAME, AlwaysAutoscalingDecider::new); + + public static AlwaysAutoscalingDecider parse(final XContentParser parser) { + return PARSER.apply(parser, null); + } + + public AlwaysAutoscalingDecider() {} + + @SuppressWarnings("unused") + public AlwaysAutoscalingDecider(final StreamInput in) { + + } + + @Override + public String name() { + return NAME; + } + + @Override + public AutoscalingDecision scale() { + return new AutoscalingDecision(NAME, AutoscalingDecisionType.SCALE_UP, "always"); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(final StreamOutput out) { + + } + + @Override + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(); + {} + builder.endObject(); + return builder; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + return true; + } + + @Override + public int hashCode() { + return 0; + } + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecider.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecider.java new file mode 100644 index 00000000000..28a9f7147bb --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecider.java @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.decision; + +import org.elasticsearch.common.io.stream.NamedWriteable; +import org.elasticsearch.common.xcontent.ToXContentObject; + +/** + * Represents an autoscaling decider, a component that determines whether or not to scale. + */ +public interface AutoscalingDecider extends ToXContentObject, NamedWriteable { + + /** + * The name of the autoscaling decider. + * + * @return the name + */ + String name(); + + /** + * Whether or not to scale based on the current state. + * + * @return the autoscaling decision + */ + AutoscalingDecision scale(); + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecision.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecision.java similarity index 97% rename from x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecision.java rename to x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecision.java index 4f4a3ce7098..af32320a3ce 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecision.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecision.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionType.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionType.java similarity index 97% rename from x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionType.java rename to x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionType.java index 8590e264262..8c930023d63 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionType.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionType.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisions.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisions.java similarity index 93% rename from x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisions.java rename to x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisions.java index 5e7e23fcc35..240b8b1f915 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisions.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisions.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -65,7 +65,7 @@ public class AutoscalingDecisions implements ToXContent, Writeable { public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - final org.elasticsearch.xpack.autoscaling.AutoscalingDecisions that = (org.elasticsearch.xpack.autoscaling.AutoscalingDecisions) o; + final AutoscalingDecisions that = (AutoscalingDecisions) o; return decisions.equals(that.decisions); } diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicy.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicy.java new file mode 100644 index 00000000000..3156358b238 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicy.java @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.policy; + +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.cluster.Diffable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecider; + +import java.io.IOException; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class AutoscalingPolicy extends AbstractDiffable implements Diffable, ToXContentObject { + + public static final String NAME = "autoscaling_policy"; + + public static final ParseField DECIDERS_FIELD = new ParseField("deciders"); + + private static final ConstructingObjectParser PARSER; + + static { + PARSER = new ConstructingObjectParser<>(NAME, false, (c, name) -> { + @SuppressWarnings("unchecked") + final List> deciders = (List>) c[0]; + return new AutoscalingPolicy( + name, + new TreeMap<>(deciders.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))) + ); + }); + PARSER.declareNamedObjects( + ConstructingObjectParser.constructorArg(), + (p, c, n) -> new AbstractMap.SimpleEntry<>(n, p.namedObject(AutoscalingDecider.class, n, null)), + DECIDERS_FIELD + ); + } + + public static AutoscalingPolicy parse(final XContentParser parser, final String name) { + return PARSER.apply(parser, name); + } + + private final String name; + + public String name() { + return name; + } + + private final SortedMap deciders; + + public SortedMap deciders() { + return deciders; + } + + public AutoscalingPolicy(final String name, final SortedMap deciders) { + this.name = Objects.requireNonNull(name); + // TODO: validate that the policy deciders are non-empty + this.deciders = Objects.requireNonNull(deciders); + } + + public AutoscalingPolicy(final StreamInput in) throws IOException { + name = in.readString(); + deciders = new TreeMap<>( + in.readNamedWriteableList(AutoscalingDecider.class) + .stream() + .collect(Collectors.toMap(AutoscalingDecider::name, Function.identity())) + ); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeString(name); + out.writeNamedWriteableList(Collections.unmodifiableList(deciders.values().stream().collect(Collectors.toList()))); + } + + @Override + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(); + { + builder.startObject(DECIDERS_FIELD.getPreferredName()); + { + for (final Map.Entry entry : deciders.entrySet()) { + builder.field(entry.getKey(), entry.getValue()); + } + } + builder.endObject(); + } + builder.endObject(); + return builder; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final AutoscalingPolicy that = (AutoscalingPolicy) o; + return name.equals(that.name) && deciders.equals(that.deciders); + } + + @Override + public int hashCode() { + return Objects.hash(name, deciders); + } + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadata.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadata.java new file mode 100644 index 00000000000..fa997999ee9 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadata.java @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.policy; + +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.cluster.Diffable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +public class AutoscalingPolicyMetadata extends AbstractDiffable + implements + Diffable, + ToXContentObject { + + static final ParseField POLICY_FIELD = new ParseField("policy"); + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER; + static { + PARSER = new ConstructingObjectParser<>("autoscaling_policy_metadata", a -> { + final AutoscalingPolicy policy = (AutoscalingPolicy) a[0]; + return new AutoscalingPolicyMetadata(policy); + }); + PARSER.declareObject(ConstructingObjectParser.constructorArg(), AutoscalingPolicy::parse, POLICY_FIELD); + } + + public static AutoscalingPolicyMetadata parse(final XContentParser parser, final String name) { + return PARSER.apply(parser, name); + } + + private final AutoscalingPolicy policy; + + public AutoscalingPolicy policy() { + return policy; + } + + public AutoscalingPolicyMetadata(final AutoscalingPolicy policy) { + this.policy = policy; + } + + public AutoscalingPolicyMetadata(final StreamInput in) throws IOException { + policy = new AutoscalingPolicy(in); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + policy.writeTo(out); + } + + @Override + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(); + { + builder.field(POLICY_FIELD.getPreferredName(), policy); + } + builder.endObject(); + return builder; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final AutoscalingPolicyMetadata that = (AutoscalingPolicyMetadata) o; + return policy.equals(that.policy); + } + + @Override + public int hashCode() { + return Objects.hash(policy); + } + +} diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/rest/RestPutAutoscalingPolicyHandler.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/rest/RestPutAutoscalingPolicyHandler.java new file mode 100644 index 00000000000..69af94bb7d9 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/rest/RestPutAutoscalingPolicyHandler.java @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.rest; + +import org.elasticsearch.client.node.NodeClient; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.xpack.autoscaling.action.PutAutoscalingPolicyAction; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.elasticsearch.rest.RestRequest.Method.PUT; + +public class RestPutAutoscalingPolicyHandler extends BaseRestHandler { + + @Override + public List routes() { + return Collections.singletonList(new Route(PUT, "/_autoscaling/policy/{name}")); + } + + @Override + public String getName() { + return "put_autoscaling_policy"; + } + + @Override + protected RestChannelConsumer prepareRequest(final RestRequest restRequest, final NodeClient client) throws IOException { + final String name = restRequest.param("name"); + final PutAutoscalingPolicyAction.Request request; + try (XContentParser parser = restRequest.contentParser()) { + request = PutAutoscalingPolicyAction.Request.parse(parser, name); + } + return channel -> client.execute(PutAutoscalingPolicyAction.INSTANCE, request, new RestToXContentListener<>(channel)); + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingIntegTestCase.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingIntegTestCase.java new file mode 100644 index 00000000000..c2d29f31959 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingIntegTestCase.java @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.xpack.core.XPackSettings; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public abstract class AutoscalingIntegTestCase extends ESIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(LocalStateAutoscaling.class); + } + + @Override + protected Settings nodeSettings(final int nodeOrdinal) { + final Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal)); + builder.put(Autoscaling.AUTOSCALING_ENABLED_SETTING.getKey(), true); + builder.put(XPackSettings.SECURITY_ENABLED.getKey(), false); + return builder.build(); + } + + @Override + protected Collection> transportClientPlugins() { + return Collections.unmodifiableList(Arrays.asList(LocalStateAutoscaling.class, getTestTransportPlugin())); + } + + @Override + protected Settings transportClientSettings() { + final Settings.Builder builder = Settings.builder().put(super.transportClientSettings()); + builder.put(Autoscaling.AUTOSCALING_ENABLED_SETTING.getKey(), true); + builder.put(XPackSettings.SECURITY_ENABLED.getKey(), false); + return builder.build(); + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadataDiffableSerializationTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadataDiffableSerializationTests.java new file mode 100644 index 00000000000..af504d88beb --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingMetadataDiffableSerializationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling; + +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractDiffableSerializationTestCase; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicyMetadata; + +import java.io.IOException; +import java.util.SortedMap; +import java.util.TreeMap; + +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.mutateAutoscalingPolicy; +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomAutoscalingMetadata; +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomAutoscalingPolicy; + +public class AutoscalingMetadataDiffableSerializationTests extends AbstractDiffableSerializationTestCase { + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry(new Autoscaling(Settings.EMPTY).getNamedWriteables()); + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + return new NamedXContentRegistry(new Autoscaling(Settings.EMPTY).getNamedXContent()); + } + + @Override + protected AutoscalingMetadata doParseInstance(final XContentParser parser) throws IOException { + return AutoscalingMetadata.parse(parser); + } + + @Override + protected Writeable.Reader instanceReader() { + return AutoscalingMetadata::new; + } + + @Override + protected AutoscalingMetadata createTestInstance() { + return randomAutoscalingMetadata(); + } + + @Override + protected Metadata.Custom makeTestChanges(final Metadata.Custom testInstance) { + return mutateInstance(testInstance); + } + + @Override + protected Metadata.Custom mutateInstance(final Metadata.Custom instance) { + final AutoscalingMetadata metadata = (AutoscalingMetadata) instance; + final SortedMap policies = new TreeMap<>(metadata.policies()); + if (policies.size() == 0 || randomBoolean()) { + final AutoscalingPolicy policy = randomAutoscalingPolicy(); + policies.put(policy.name(), new AutoscalingPolicyMetadata(policy)); + } else { + // randomly remove a policy + final String name = randomFrom(policies.keySet()); + final AutoscalingPolicyMetadata policyMetadata = policies.remove(name); + final AutoscalingPolicy mutatedPolicy = mutateAutoscalingPolicy(policyMetadata.policy()); + policies.put(mutatedPolicy.name(), new AutoscalingPolicyMetadata(mutatedPolicy)); + } + return new AutoscalingMetadata(policies); + } + + @Override + protected Writeable.Reader> diffReader() { + return AutoscalingMetadata.AutoscalingMetadataDiff::new; + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingTestCase.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingTestCase.java index 29b9a3c41bd..a6dadbce62d 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingTestCase.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingTestCase.java @@ -8,21 +8,34 @@ package org.elasticsearch.xpack.autoscaling; import org.elasticsearch.common.Randomness; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.autoscaling.decision.AlwaysAutoscalingDecider; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecider; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecision; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisionType; +import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisions; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicyMetadata; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; public abstract class AutoscalingTestCase extends ESTestCase { - static AutoscalingDecision randomAutoscalingDecision() { + public static AutoscalingDecision randomAutoscalingDecision() { return randomAutoscalingDecisionOfType(randomFrom(AutoscalingDecisionType.values())); } - static AutoscalingDecision randomAutoscalingDecisionOfType(final AutoscalingDecisionType type) { + public static AutoscalingDecision randomAutoscalingDecisionOfType(final AutoscalingDecisionType type) { return new AutoscalingDecision(randomAlphaOfLength(8), type, randomAlphaOfLength(8)); } - static AutoscalingDecisions randomAutoscalingDecisions() { + public static AutoscalingDecisions randomAutoscalingDecisions() { final int numberOfDecisions = 1 + randomIntBetween(1, 8); final List decisions = new ArrayList<>(numberOfDecisions); for (int i = 0; i < numberOfDecisions; i++) { @@ -34,7 +47,7 @@ public abstract class AutoscalingTestCase extends ESTestCase { return randomAutoscalingDecisions(numberOfDownDecisions, numberOfNoDecisions, numberOfUpDecisions); } - static AutoscalingDecisions randomAutoscalingDecisions( + public static AutoscalingDecisions randomAutoscalingDecisions( final int numberOfDownDecisions, final int numberOfNoDecisions, final int numberOfUpDecisions @@ -53,4 +66,57 @@ public abstract class AutoscalingTestCase extends ESTestCase { return new AutoscalingDecisions(decisions); } + public static SortedMap randomAutoscalingDeciders() { + return new TreeMap<>( + Collections.singletonList(new AlwaysAutoscalingDecider()) + .stream() + .collect(Collectors.toMap(AutoscalingDecider::name, Function.identity())) + ); + } + + public static AutoscalingPolicy randomAutoscalingPolicy() { + return randomAutoscalingPolicyOfName(randomAlphaOfLength(8)); + } + + public static AutoscalingPolicy randomAutoscalingPolicyOfName(final String name) { + return new AutoscalingPolicy(name, randomAutoscalingDeciders()); + } + + public static AutoscalingPolicy mutateAutoscalingPolicy(final AutoscalingPolicy instance) { + final SortedMap deciders; + if (randomBoolean()) { + // if the policy name did not change, or randomly, use a mutated set of deciders + deciders = mutateAutoscalingDeciders(instance.deciders()); + } else { + deciders = instance.deciders(); + } + return new AutoscalingPolicy(randomValueOtherThan(instance.name(), () -> randomAlphaOfLength(8)), deciders); + } + + public static SortedMap mutateAutoscalingDeciders(final SortedMap deciders) { + if (deciders.size() == 0) { + return randomAutoscalingDeciders(); + } else { + // use a proper subset of the deciders + return new TreeMap<>( + randomSubsetOf(randomIntBetween(0, deciders.size() - 1), deciders.entrySet()).stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + ); + } + } + + public static AutoscalingMetadata randomAutoscalingMetadata() { + return randomAutoscalingMetadataOfPolicyCount(randomIntBetween(0, 8)); + } + + public static AutoscalingMetadata randomAutoscalingMetadataOfPolicyCount(final int numberOfPolicies) { + final SortedMap policies = new TreeMap<>(); + for (int i = 0; i < numberOfPolicies; i++) { + final AutoscalingPolicy policy = randomAutoscalingPolicy(); + final AutoscalingPolicyMetadata policyMetadata = new AutoscalingPolicyMetadata(policy); + policies.put(policy.name(), policyMetadata); + } + return new AutoscalingMetadata(policies); + } + } diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/LocalStateAutoscaling.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/LocalStateAutoscaling.java new file mode 100644 index 00000000000..dbd23669aa9 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/LocalStateAutoscaling.java @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; + +public class LocalStateAutoscaling extends LocalStateCompositeXPackPlugin { + + public LocalStateAutoscaling(final Settings settings) { + super(settings, null); + plugins.add(new Autoscaling(settings) { + + @Override + protected XPackLicenseState getLicenseState() { + return LocalStateAutoscaling.this.getLicenseState(); + } + + }); + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionIT.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionIT.java new file mode 100644 index 00000000000..d58f6606535 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionIT.java @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.action; + +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.xpack.autoscaling.AutoscalingIntegTestCase; +import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata; +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.randomAutoscalingPolicy; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.sameInstance; + +public class TransportPutAutoscalingPolicyActionIT extends AutoscalingIntegTestCase { + + public void testAddPolicy() { + final AutoscalingPolicy policy = putRandomAutoscalingPolicy(); + final ClusterState state = client().admin().cluster().prepareState().get().getState(); + final AutoscalingMetadata metadata = state.metadata().custom(AutoscalingMetadata.NAME); + assertNotNull(metadata); + assertThat(metadata.policies(), hasKey(policy.name())); + assertThat(metadata.policies().get(policy.name()).policy(), equalTo(policy)); + } + + public void testUpdatePolicy() { + final AutoscalingPolicy policy = putRandomAutoscalingPolicy(); + final AutoscalingPolicy updatedPolicy = new AutoscalingPolicy(policy.name(), mutateAutoscalingDeciders(policy.deciders())); + putAutoscalingPolicy(updatedPolicy); + final ClusterState state = client().admin().cluster().prepareState().get().getState(); + final AutoscalingMetadata metadata = state.metadata().custom(AutoscalingMetadata.NAME); + assertNotNull(metadata); + assertThat(metadata.policies(), hasKey(policy.name())); + assertThat(metadata.policies().get(policy.name()).policy(), equalTo(updatedPolicy)); + } + + public void testNoOpPolicy() { + final AutoscalingPolicy policy = putRandomAutoscalingPolicy(); + final ClusterState beforeState = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()).state(); + putAutoscalingPolicy(policy); + final ClusterState afterState = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()).state(); + assertThat( + beforeState.metadata().custom(AutoscalingMetadata.NAME), + sameInstance(afterState.metadata().custom(AutoscalingMetadata.NAME)) + ); + } + + private AutoscalingPolicy putRandomAutoscalingPolicy() { + final AutoscalingPolicy policy = randomAutoscalingPolicy(); + putAutoscalingPolicy(policy); + return policy; + } + + private void putAutoscalingPolicy(final AutoscalingPolicy policy) { + final PutAutoscalingPolicyAction.Request request = new PutAutoscalingPolicyAction.Request(policy); + assertAcked(client().execute(PutAutoscalingPolicyAction.INSTANCE, request).actionGet()); + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionTests.java new file mode 100644 index 00000000000..b4008914318 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyActionTests.java @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.action; + +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlocks; +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.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata; +import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy; +import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicyMetadata; + +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.sameInstance; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +public class TransportPutAutoscalingPolicyActionTests extends AutoscalingTestCase { + + public void testWriteBlock() { + final TransportPutAutoscalingPolicyAction action = new TransportPutAutoscalingPolicyAction( + mock(TransportService.class), + mock(ClusterService.class), + mock(ThreadPool.class), + mock(ActionFilters.class), + mock(IndexNameExpressionResolver.class) + ); + final ClusterBlocks blocks = ClusterBlocks.builder() + .addGlobalBlock( + randomFrom( + Metadata.CLUSTER_READ_ONLY_BLOCK, + Metadata.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK, + NoMasterBlockService.NO_MASTER_BLOCK_WRITES + ) + ) + .build(); + final ClusterState state = ClusterState.builder(new ClusterName(randomAlphaOfLength(8))).blocks(blocks).build(); + final ClusterBlockException e = action.checkBlock(new PutAutoscalingPolicyAction.Request(randomAutoscalingPolicy()), state); + assertThat(e, not(nullValue())); + } + + public void testNoWriteBlock() { + final TransportPutAutoscalingPolicyAction action = new TransportPutAutoscalingPolicyAction( + mock(TransportService.class), + mock(ClusterService.class), + mock(ThreadPool.class), + mock(ActionFilters.class), + mock(IndexNameExpressionResolver.class) + ); + final ClusterBlocks blocks = ClusterBlocks.builder().build(); + final ClusterState state = ClusterState.builder(new ClusterName(randomAlphaOfLength(8))).blocks(blocks).build(); + final ClusterBlockException e = action.checkBlock(new PutAutoscalingPolicyAction.Request(randomAutoscalingPolicy()), state); + assertThat(e, nullValue()); + } + + public void testAddPolicy() { + final ClusterState currentState; + { + final ClusterState.Builder builder = ClusterState.builder(new ClusterName(randomAlphaOfLength(8))); + if (randomBoolean()) { + builder.metadata(Metadata.builder().putCustom(AutoscalingMetadata.NAME, randomAutoscalingMetadata())); + } + currentState = builder.build(); + } + // put an entirely new policy + final AutoscalingPolicy policy = randomAutoscalingPolicy(); + final Logger mockLogger = mock(Logger.class); + final ClusterState state = TransportPutAutoscalingPolicyAction.putAutoscalingPolicy(currentState, policy, mockLogger); + + // ensure the new policy is in the updated cluster state + final AutoscalingMetadata metadata = state.metadata().custom(AutoscalingMetadata.NAME); + assertNotNull(metadata); + assertThat(metadata.policies(), hasKey(policy.name())); + assertThat(metadata.policies().get(policy.name()).policy(), equalTo(policy)); + verify(mockLogger).info("adding autoscaling policy [{}]", policy.name()); + verifyNoMoreInteractions(mockLogger); + + // ensure that existing policies were preserved + final AutoscalingMetadata currentMetadata = currentState.metadata().custom(AutoscalingMetadata.NAME); + if (currentMetadata != null) { + for (final Map.Entry entry : currentMetadata.policies().entrySet()) { + assertThat(metadata.policies(), hasKey(entry.getKey())); + assertThat(metadata.policies().get(entry.getKey()).policy(), equalTo(entry.getValue().policy())); + } + } + } + + public void testUpdatePolicy() { + 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 name = randomFrom(currentMetadata.policies().keySet()); + // add to the existing deciders, to ensure the policy has changed + final AutoscalingPolicy policy = new AutoscalingPolicy( + name, + mutateAutoscalingDeciders(currentMetadata.policies().get(name).policy().deciders()) + ); + final Logger mockLogger = mock(Logger.class); + final ClusterState state = TransportPutAutoscalingPolicyAction.putAutoscalingPolicy(currentState, policy, mockLogger); + + // ensure the updated policy is in the updated cluster state + final AutoscalingMetadata metadata = state.metadata().custom(AutoscalingMetadata.NAME); + assertNotNull(metadata); + assertThat(metadata.policies(), hasKey(policy.name())); + assertThat(metadata.policies().get(policy.name()).policy(), equalTo(policy)); + verify(mockLogger).info("updating autoscaling policy [{}]", policy.name()); + verifyNoMoreInteractions(mockLogger); + + // ensure that existing policies were otherwise preserved + for (final Map.Entry entry : currentMetadata.policies().entrySet()) { + if (entry.getKey().equals(name)) { + continue; + } + assertThat(metadata.policies(), hasKey(entry.getKey())); + assertThat(metadata.policies().get(entry.getKey()).policy(), equalTo(entry.getValue().policy())); + } + } + + public void testNoOpUpdatePolicy() { + 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(); + } + // randomly put an existing policy + final AutoscalingMetadata currentMetadata = currentState.metadata().custom(AutoscalingMetadata.NAME); + final AutoscalingPolicy policy = randomFrom(currentMetadata.policies().values()).policy(); + final Logger mockLogger = mock(Logger.class); + final ClusterState state = TransportPutAutoscalingPolicyAction.putAutoscalingPolicy(currentState, policy, mockLogger); + + assertThat(state, sameInstance(currentState)); + verify(mockLogger).info("skipping updating autoscaling policy [{}] due to no change in policy", policy.name()); + verifyNoMoreInteractions(mockLogger); + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionTests.java similarity index 90% rename from x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionTests.java rename to x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionTests.java index c6c8dfa94d1..52abe427bf4 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionTests.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionTests.java @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase; import java.io.IOException; diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionTypeWireSerializingTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionTypeWireSerializingTests.java similarity index 97% rename from x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionTypeWireSerializingTests.java rename to x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionTypeWireSerializingTests.java index caa5c6440bb..7fa928bbfba 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionTypeWireSerializingTests.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionTypeWireSerializingTests.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.Writeable; diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionWireSerializingTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionWireSerializingTests.java similarity index 86% rename from x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionWireSerializingTests.java rename to x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionWireSerializingTests.java index f24dbcabdd0..b2276b671d4 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionWireSerializingTests.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionWireSerializingTests.java @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.test.AbstractWireSerializingTestCase; +import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase; public class AutoscalingDecisionWireSerializingTests extends AbstractWireSerializingTestCase { diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionsTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionsTests.java similarity index 92% rename from x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionsTests.java rename to x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionsTests.java index 8eb9c227203..df0e551578e 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionsTests.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionsTests.java @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; + +import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase; import java.util.Collections; diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionsWireSerializingTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionsWireSerializingTests.java similarity index 86% rename from x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionsWireSerializingTests.java rename to x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionsWireSerializingTests.java index c058d7c81d5..120ce387d55 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/AutoscalingDecisionsWireSerializingTests.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/decision/AutoscalingDecisionsWireSerializingTests.java @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.autoscaling; +package org.elasticsearch.xpack.autoscaling.decision; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.test.AbstractWireSerializingTestCase; +import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase; public class AutoscalingDecisionsWireSerializingTests extends AbstractWireSerializingTestCase { diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadataDiffableSerializationTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadataDiffableSerializationTests.java new file mode 100644 index 00000000000..931e5da2478 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicyMetadataDiffableSerializationTests.java @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.policy; + +import org.elasticsearch.cluster.AbstractDiffable; +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractDiffableSerializationTestCase; +import org.elasticsearch.xpack.autoscaling.Autoscaling; + +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.mutateAutoscalingPolicy; +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomAutoscalingPolicyOfName; + +public class AutoscalingPolicyMetadataDiffableSerializationTests extends AbstractDiffableSerializationTestCase { + + private final String name = randomAlphaOfLength(8); + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry(new Autoscaling(Settings.EMPTY).getNamedWriteables()); + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + return new NamedXContentRegistry(new Autoscaling(Settings.EMPTY).getNamedXContent()); + } + + @Override + protected AutoscalingPolicyMetadata doParseInstance(final XContentParser parser) { + return AutoscalingPolicyMetadata.parse(parser, name); + } + + @Override + protected Writeable.Reader instanceReader() { + return AutoscalingPolicyMetadata::new; + } + + @Override + protected AutoscalingPolicyMetadata createTestInstance() { + return new AutoscalingPolicyMetadata(randomAutoscalingPolicyOfName(name)); + } + + @Override + protected AutoscalingPolicyMetadata makeTestChanges(final AutoscalingPolicyMetadata testInstance) { + return mutateInstance(testInstance); + } + + @Override + protected AutoscalingPolicyMetadata mutateInstance(final AutoscalingPolicyMetadata instance) { + return new AutoscalingPolicyMetadata(mutateAutoscalingPolicy(instance.policy())); + } + + @Override + protected Writeable.Reader> diffReader() { + return in -> AbstractDiffable.readDiffFrom(AutoscalingPolicyMetadata::new, in); + } + +} diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicySerializingTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicySerializingTests.java new file mode 100644 index 00000000000..0d49ccc4e21 --- /dev/null +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/policy/AutoscalingPolicySerializingTests.java @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.autoscaling.policy; + +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractSerializingTestCase; +import org.elasticsearch.xpack.autoscaling.Autoscaling; + +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.mutateAutoscalingPolicy; +import static org.elasticsearch.xpack.autoscaling.AutoscalingTestCase.randomAutoscalingPolicyOfName; + +public class AutoscalingPolicySerializingTests extends AbstractSerializingTestCase { + + private final String name = randomAlphaOfLength(8); + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry(new Autoscaling(Settings.EMPTY).getNamedWriteables()); + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + return new NamedXContentRegistry(new Autoscaling(Settings.EMPTY).getNamedXContent()); + } + + @Override + protected AutoscalingPolicy doParseInstance(final XContentParser parser) { + return AutoscalingPolicy.parse(parser, name); + } + + @Override + protected Writeable.Reader instanceReader() { + return AutoscalingPolicy::new; + } + + @Override + protected AutoscalingPolicy createTestInstance() { + return randomAutoscalingPolicyOfName(name); + } + + @Override + protected AutoscalingPolicy mutateInstance(final AutoscalingPolicy instance) { + return mutateAutoscalingPolicy(instance); + } + +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java index 192f74afede..d2de6b902d7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java @@ -102,7 +102,7 @@ public class LocalStateCompositeXPackPlugin extends XPackPlugin implements Scrip private LicenseService licenseService; protected List plugins = new ArrayList<>(); - public LocalStateCompositeXPackPlugin(final Settings settings, final Path configPath) throws Exception { + public LocalStateCompositeXPackPlugin(final Settings settings, final Path configPath) { super(settings, configPath); } diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/api/autoscaling.put_autoscaling_policy.json b/x-pack/plugin/src/test/resources/rest-api-spec/api/autoscaling.put_autoscaling_policy.json new file mode 100644 index 00000000000..b6258cdf59e --- /dev/null +++ b/x-pack/plugin/src/test/resources/rest-api-spec/api/autoscaling.put_autoscaling_policy.json @@ -0,0 +1,28 @@ +{ + "autoscaling.put_autoscaling_policy":{ + "documentation":{ + "url":"https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-put-autoscaling-policy.html" + }, + "stability":"experimental", + "url":{ + "paths":[ + { + "path":"/_autoscaling/policy/{name}", + "methods":[ + "PUT" + ], + "parts":{ + "name":{ + "type":"string", + "description":"the name of the autoscaling policy" + } + } + } + ] + }, + "body":{ + "description":"the specification of the autoscaling policy", + "required":true + } + } +}