Split the autoscaling decider into a service and configuration in order to enable having additional context information available in the service. Added AutoscalingDeciderContext holding generic information all deciders are expected to need. Implemented GET _autoscaling/decision
This commit is contained in:
parent
8a0f1d8746
commit
a155315ceb
|
@ -25,6 +25,17 @@ PUT /_autoscaling/policy/my_autoscaling_policy
|
|||
--------------------------------------------------
|
||||
// TESTSETUP
|
||||
|
||||
//////////////////////////
|
||||
|
||||
[source,console]
|
||||
--------------------------------------------------
|
||||
DELETE /_autoscaling/policy/my_autoscaling_policy
|
||||
--------------------------------------------------
|
||||
// TEST
|
||||
// TEARDOWN
|
||||
|
||||
//////////////////////////
|
||||
|
||||
[source,console]
|
||||
--------------------------------------------------
|
||||
GET /_autoscaling/policy/<name>
|
||||
|
|
|
@ -25,6 +25,16 @@ PUT /_autoscaling/policy/<name>
|
|||
--------------------------------------------------
|
||||
// TEST[s/<name>/name/]
|
||||
|
||||
//////////////////////////
|
||||
|
||||
[source,console]
|
||||
--------------------------------------------------
|
||||
DELETE /_autoscaling/policy/name
|
||||
--------------------------------------------------
|
||||
// TEST[continued]
|
||||
|
||||
//////////////////////////
|
||||
|
||||
[[autoscaling-put-autoscaling-policy-prereqs]]
|
||||
==== {api-prereq-title}
|
||||
|
||||
|
@ -65,3 +75,13 @@ The API returns the following result:
|
|||
"acknowledged": true
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
//////////////////////////
|
||||
|
||||
[source,console]
|
||||
--------------------------------------------------
|
||||
DELETE /_autoscaling/policy/my_autoscaling_policy
|
||||
--------------------------------------------------
|
||||
// TEST[continued]
|
||||
|
||||
//////////////////////////
|
||||
|
|
|
@ -1,6 +1,31 @@
|
|||
---
|
||||
"Test get autoscaling decision":
|
||||
"Test get empty autoscaling decision":
|
||||
- do:
|
||||
autoscaling.get_autoscaling_decision: {}
|
||||
|
||||
- match: { "decisions": [] }
|
||||
|
||||
---
|
||||
"Test get always autoscaling decision":
|
||||
- do:
|
||||
autoscaling.put_autoscaling_policy:
|
||||
name: my_autoscaling_policy
|
||||
body:
|
||||
policy:
|
||||
deciders:
|
||||
always: {}
|
||||
|
||||
- match: { "acknowledged": true }
|
||||
|
||||
- do:
|
||||
autoscaling.get_autoscaling_decision: {}
|
||||
|
||||
- match: { decisions.0.my_autoscaling_policy.decision: scale_up }
|
||||
- match: { decisions.0.my_autoscaling_policy.decisions.0.name: always }
|
||||
- match: { decisions.0.my_autoscaling_policy.decisions.0.type: scale_up }
|
||||
- match: { decisions.0.my_autoscaling_policy.decisions.0.reason: always }
|
||||
|
||||
# test cleanup
|
||||
- do:
|
||||
autoscaling.delete_autoscaling_policy:
|
||||
name: my_autoscaling_policy
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
|
||||
- match: { "acknowledged": true }
|
||||
|
||||
# test cleanup
|
||||
- do:
|
||||
autoscaling.delete_autoscaling_policy:
|
||||
name: my_autoscaling_policy
|
||||
|
||||
---
|
||||
"Test put autoscaling policy with non-existent decider":
|
||||
- do:
|
||||
|
|
|
@ -6,13 +6,17 @@
|
|||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.elasticsearch.Build;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
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.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.ClusterSettings;
|
||||
|
@ -21,11 +25,18 @@ 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.env.Environment;
|
||||
import org.elasticsearch.env.NodeEnvironment;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.plugins.ActionPlugin;
|
||||
import org.elasticsearch.plugins.ExtensiblePlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.repositories.RepositoriesService;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestHandler;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.autoscaling.action.DeleteAutoscalingPolicyAction;
|
||||
import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingDecisionAction;
|
||||
import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingPolicyAction;
|
||||
|
@ -34,22 +45,29 @@ import org.elasticsearch.xpack.autoscaling.action.TransportDeleteAutoscalingPoli
|
|||
import org.elasticsearch.xpack.autoscaling.action.TransportGetAutoscalingDecisionAction;
|
||||
import org.elasticsearch.xpack.autoscaling.action.TransportGetAutoscalingPolicyAction;
|
||||
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.decision.AlwaysAutoscalingDeciderConfiguration;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AlwaysAutoscalingDeciderService;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDeciderConfiguration;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDeciderService;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisionService;
|
||||
import org.elasticsearch.xpack.autoscaling.rest.RestDeleteAutoscalingPolicyHandler;
|
||||
import org.elasticsearch.xpack.autoscaling.rest.RestGetAutoscalingDecisionHandler;
|
||||
import org.elasticsearch.xpack.autoscaling.rest.RestGetAutoscalingPolicyHandler;
|
||||
import org.elasticsearch.xpack.autoscaling.rest.RestPutAutoscalingPolicyHandler;
|
||||
import org.elasticsearch.xpack.core.XPackPlugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Container class for autoscaling functionality.
|
||||
*/
|
||||
public class Autoscaling extends Plugin implements ActionPlugin {
|
||||
|
||||
public class Autoscaling extends Plugin implements ActionPlugin, ExtensiblePlugin, AutoscalingExtension {
|
||||
private static final Logger logger = LogManager.getLogger(AutoscalingExtension.class);
|
||||
private static final Boolean AUTOSCALING_FEATURE_FLAG_REGISTERED;
|
||||
|
||||
static {
|
||||
|
@ -78,8 +96,11 @@ public class Autoscaling extends Plugin implements ActionPlugin {
|
|||
|
||||
private final boolean enabled;
|
||||
|
||||
private final List<AutoscalingExtension> autoscalingExtensions;
|
||||
|
||||
public Autoscaling(final Settings settings) {
|
||||
this.enabled = AUTOSCALING_ENABLED_SETTING.get(settings);
|
||||
this.autoscalingExtensions = new ArrayList<>(org.elasticsearch.common.collect.List.of(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,6 +121,23 @@ public class Autoscaling extends Plugin implements ActionPlugin {
|
|||
return Build.CURRENT.isSnapshot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> createComponents(
|
||||
Client client,
|
||||
ClusterService clusterService,
|
||||
ThreadPool threadPool,
|
||||
ResourceWatcherService resourceWatcherService,
|
||||
ScriptService scriptService,
|
||||
NamedXContentRegistry xContentRegistry,
|
||||
Environment environment,
|
||||
NodeEnvironment nodeEnvironment,
|
||||
NamedWriteableRegistry namedWriteableRegistry,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
Supplier<RepositoriesService> repositoriesServiceSupplier
|
||||
) {
|
||||
return org.elasticsearch.common.collect.List.of(new AutoscalingDecisionService.Holder(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
|
||||
if (enabled) {
|
||||
|
@ -141,7 +179,11 @@ public class Autoscaling extends Plugin implements ActionPlugin {
|
|||
return org.elasticsearch.common.collect.List.of(
|
||||
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)
|
||||
new NamedWriteableRegistry.Entry(
|
||||
AutoscalingDeciderConfiguration.class,
|
||||
AlwaysAutoscalingDeciderConfiguration.NAME,
|
||||
AlwaysAutoscalingDeciderConfiguration::new
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -150,9 +192,9 @@ public class Autoscaling extends Plugin implements ActionPlugin {
|
|||
return org.elasticsearch.common.collect.List.of(
|
||||
new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(AutoscalingMetadata.NAME), AutoscalingMetadata::parse),
|
||||
new NamedXContentRegistry.Entry(
|
||||
AutoscalingDecider.class,
|
||||
new ParseField(AlwaysAutoscalingDecider.NAME),
|
||||
AlwaysAutoscalingDecider::parse
|
||||
AutoscalingDeciderConfiguration.class,
|
||||
new ParseField(AlwaysAutoscalingDeciderConfiguration.NAME),
|
||||
AlwaysAutoscalingDeciderConfiguration::parse
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -161,4 +203,17 @@ public class Autoscaling extends Plugin implements ActionPlugin {
|
|||
return XPackPlugin.getSharedLicenseState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadExtensions(ExtensionLoader loader) {
|
||||
loader.loadExtensions(AutoscalingExtension.class).forEach(autoscalingExtensions::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AutoscalingDeciderService<? extends AutoscalingDeciderConfiguration>> deciders() {
|
||||
return org.elasticsearch.common.collect.List.of(new AlwaysAutoscalingDeciderService());
|
||||
}
|
||||
|
||||
public Set<AutoscalingDeciderService<? extends AutoscalingDeciderConfiguration>> createDeciderServices() {
|
||||
return autoscalingExtensions.stream().flatMap(p -> p.deciders().stream()).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.xpack.autoscaling.decision.AutoscalingDeciderConfiguration;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDeciderService;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface AutoscalingExtension {
|
||||
/**
|
||||
* Get the list of decider services for this plugin. This is called after createComponents has been called.
|
||||
* @return list of decider services
|
||||
*/
|
||||
Collection<AutoscalingDeciderService<? extends AutoscalingDeciderConfiguration>> deciders();
|
||||
}
|
|
@ -17,22 +17,24 @@ 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.decision.AutoscalingDecisionService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAction<
|
||||
GetAutoscalingDecisionAction.Request,
|
||||
GetAutoscalingDecisionAction.Response> {
|
||||
|
||||
private final AutoscalingDecisionService decisionService;
|
||||
|
||||
@Inject
|
||||
public TransportGetAutoscalingDecisionAction(
|
||||
final TransportService transportService,
|
||||
final ClusterService clusterService,
|
||||
final ThreadPool threadPool,
|
||||
final ActionFilters actionFilters,
|
||||
final IndexNameExpressionResolver indexNameExpressionResolver
|
||||
final IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
final AutoscalingDecisionService.Holder decisionServiceHolder
|
||||
) {
|
||||
super(
|
||||
GetAutoscalingDecisionAction.NAME,
|
||||
|
@ -43,6 +45,8 @@ public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAc
|
|||
GetAutoscalingDecisionAction.Request::new,
|
||||
indexNameExpressionResolver
|
||||
);
|
||||
this.decisionService = decisionServiceHolder.get();
|
||||
assert this.decisionService != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,7 +65,7 @@ public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAc
|
|||
final ClusterState state,
|
||||
final ActionListener<GetAutoscalingDecisionAction.Response> listener
|
||||
) {
|
||||
listener.onResponse(new GetAutoscalingDecisionAction.Response(Collections.unmodifiableSortedMap(new TreeMap<>())));
|
||||
listener.onResponse(new GetAutoscalingDecisionAction.Response(decisionService.decide(state)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,20 +14,23 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AlwaysAutoscalingDecider implements AutoscalingDecider {
|
||||
public class AlwaysAutoscalingDeciderConfiguration implements AutoscalingDeciderConfiguration {
|
||||
|
||||
public static final String NAME = "always";
|
||||
|
||||
private static final ObjectParser<AlwaysAutoscalingDecider, Void> PARSER = new ObjectParser<>(NAME, AlwaysAutoscalingDecider::new);
|
||||
private static final ObjectParser<AlwaysAutoscalingDeciderConfiguration, Void> PARSER = new ObjectParser<>(
|
||||
NAME,
|
||||
AlwaysAutoscalingDeciderConfiguration::new
|
||||
);
|
||||
|
||||
public static AlwaysAutoscalingDecider parse(final XContentParser parser) {
|
||||
public static AlwaysAutoscalingDeciderConfiguration parse(final XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public AlwaysAutoscalingDecider() {}
|
||||
public AlwaysAutoscalingDeciderConfiguration() {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public AlwaysAutoscalingDecider(final StreamInput in) {
|
||||
public AlwaysAutoscalingDeciderConfiguration(final StreamInput in) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -36,11 +39,6 @@ public class AlwaysAutoscalingDecider implements AutoscalingDecider {
|
|||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoscalingDecision scale() {
|
||||
return new AutoscalingDecision(NAME, AutoscalingDecisionType.SCALE_UP, "always");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.inject.Inject;
|
||||
|
||||
public class AlwaysAutoscalingDeciderService implements AutoscalingDeciderService<AlwaysAutoscalingDeciderConfiguration> {
|
||||
|
||||
@Inject
|
||||
public AlwaysAutoscalingDeciderService() {}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return AlwaysAutoscalingDeciderConfiguration.NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoscalingDecision scale(AlwaysAutoscalingDeciderConfiguration decider, AutoscalingDeciderContext context) {
|
||||
return new AutoscalingDecision(AlwaysAutoscalingDeciderConfiguration.NAME, AutoscalingDecisionType.SCALE_UP, "always");
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ 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 {
|
||||
public interface AutoscalingDeciderConfiguration extends ToXContentObject, NamedWriteable {
|
||||
|
||||
/**
|
||||
* The name of the autoscaling decider.
|
||||
|
@ -20,12 +20,4 @@ public interface AutoscalingDecider extends ToXContentObject, NamedWriteable {
|
|||
* @return the name
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Whether or not to scale based on the current state.
|
||||
*
|
||||
* @return the autoscaling decision
|
||||
*/
|
||||
AutoscalingDecision scale();
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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.cluster.ClusterState;
|
||||
|
||||
public interface AutoscalingDeciderContext {
|
||||
ClusterState state();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A service to decide for a specific decider.
|
||||
*/
|
||||
public interface AutoscalingDeciderService<D extends AutoscalingDeciderConfiguration> {
|
||||
|
||||
/**
|
||||
* The name of the autoscaling decider.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Whether or not to scale based on the current state.
|
||||
*
|
||||
* @param context provides access to information about current state
|
||||
* @return the autoscaling decision
|
||||
*/
|
||||
AutoscalingDecision scale(D decider, AutoscalingDeciderContext context);
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.apache.lucene.util.SetOnce;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.xpack.autoscaling.Autoscaling;
|
||||
import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata;
|
||||
import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AutoscalingDecisionService {
|
||||
private Map<String, AutoscalingDeciderService<? extends AutoscalingDeciderConfiguration>> deciderByName;
|
||||
|
||||
public AutoscalingDecisionService(Set<AutoscalingDeciderService<? extends AutoscalingDeciderConfiguration>> deciders) {
|
||||
assert deciders.size() >= 1; // always have always
|
||||
this.deciderByName = deciders.stream().collect(Collectors.toMap(AutoscalingDeciderService::name, Function.identity()));
|
||||
}
|
||||
|
||||
public static class Holder {
|
||||
private final Autoscaling autoscaling;
|
||||
private final SetOnce<AutoscalingDecisionService> servicesSetOnce = new SetOnce<>();
|
||||
|
||||
public Holder(Autoscaling autoscaling) {
|
||||
this.autoscaling = autoscaling;
|
||||
}
|
||||
|
||||
public AutoscalingDecisionService get() {
|
||||
// defer constructing services until transport action creation time.
|
||||
AutoscalingDecisionService autoscalingDecisionService = servicesSetOnce.get();
|
||||
if (autoscalingDecisionService == null) {
|
||||
autoscalingDecisionService = new AutoscalingDecisionService(autoscaling.createDeciderServices());
|
||||
servicesSetOnce.set(autoscalingDecisionService);
|
||||
}
|
||||
|
||||
return autoscalingDecisionService;
|
||||
}
|
||||
}
|
||||
|
||||
public SortedMap<String, AutoscalingDecisions> decide(ClusterState state) {
|
||||
AutoscalingDeciderContext context = () -> state;
|
||||
|
||||
AutoscalingMetadata autoscalingMetadata = state.metadata().custom(AutoscalingMetadata.NAME);
|
||||
if (autoscalingMetadata != null) {
|
||||
return new TreeMap<>(
|
||||
autoscalingMetadata.policies()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(e -> Tuple.tuple(e.getKey(), getDecision(e.getValue().policy(), context)))
|
||||
.collect(Collectors.toMap(Tuple::v1, Tuple::v2))
|
||||
);
|
||||
} else {
|
||||
return new TreeMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
private AutoscalingDecisions getDecision(AutoscalingPolicy policy, AutoscalingDeciderContext context) {
|
||||
Collection<AutoscalingDecision> decisions = policy.deciders()
|
||||
.values()
|
||||
.stream()
|
||||
.map(decider -> getDecision(decider, context))
|
||||
.collect(Collectors.toList());
|
||||
return new AutoscalingDecisions(decisions);
|
||||
}
|
||||
|
||||
private <T extends AutoscalingDeciderConfiguration> AutoscalingDecision getDecision(T decider, AutoscalingDeciderContext context) {
|
||||
assert deciderByName.containsKey(decider.name());
|
||||
@SuppressWarnings("unchecked")
|
||||
AutoscalingDeciderService<T> service = (AutoscalingDeciderService<T>) deciderByName.get(decider.name());
|
||||
return service.scale(decider, context);
|
||||
}
|
||||
}
|
|
@ -46,9 +46,10 @@ public class AutoscalingDecisions implements ToXContent, Writeable {
|
|||
|
||||
@Override
|
||||
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
|
||||
for (final AutoscalingDecision decision : decisions) {
|
||||
decision.toXContent(builder, params);
|
||||
}
|
||||
builder.startObject();
|
||||
builder.field("decision", type());
|
||||
builder.array("decisions", decisions.toArray());
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ 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 org.elasticsearch.xpack.autoscaling.decision.AutoscalingDeciderConfiguration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractMap;
|
||||
|
@ -39,7 +39,9 @@ public class AutoscalingPolicy extends AbstractDiffable<AutoscalingPolicy> imple
|
|||
static {
|
||||
PARSER = new ConstructingObjectParser<>(NAME, false, (c, name) -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<Map.Entry<String, AutoscalingDecider>> deciders = (List<Map.Entry<String, AutoscalingDecider>>) c[0];
|
||||
List<Map.Entry<String, AutoscalingDeciderConfiguration>> deciders =
|
||||
// help spotless format this
|
||||
(List<Map.Entry<String, AutoscalingDeciderConfiguration>>) c[0];
|
||||
return new AutoscalingPolicy(
|
||||
name,
|
||||
new TreeMap<>(deciders.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
|
||||
|
@ -47,7 +49,7 @@ public class AutoscalingPolicy extends AbstractDiffable<AutoscalingPolicy> imple
|
|||
});
|
||||
PARSER.declareNamedObjects(
|
||||
ConstructingObjectParser.constructorArg(),
|
||||
(p, c, n) -> new AbstractMap.SimpleEntry<>(n, p.namedObject(AutoscalingDecider.class, n, null)),
|
||||
(p, c, n) -> new AbstractMap.SimpleEntry<>(n, p.namedObject(AutoscalingDeciderConfiguration.class, n, null)),
|
||||
DECIDERS_FIELD
|
||||
);
|
||||
}
|
||||
|
@ -62,13 +64,13 @@ public class AutoscalingPolicy extends AbstractDiffable<AutoscalingPolicy> imple
|
|||
return name;
|
||||
}
|
||||
|
||||
private final SortedMap<String, AutoscalingDecider> deciders;
|
||||
private final SortedMap<String, AutoscalingDeciderConfiguration> deciders;
|
||||
|
||||
public SortedMap<String, AutoscalingDecider> deciders() {
|
||||
public SortedMap<String, AutoscalingDeciderConfiguration> deciders() {
|
||||
return deciders;
|
||||
}
|
||||
|
||||
public AutoscalingPolicy(final String name, final SortedMap<String, AutoscalingDecider> deciders) {
|
||||
public AutoscalingPolicy(final String name, final SortedMap<String, AutoscalingDeciderConfiguration> deciders) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
// TODO: validate that the policy deciders are non-empty
|
||||
this.deciders = Objects.requireNonNull(deciders);
|
||||
|
@ -77,9 +79,9 @@ public class AutoscalingPolicy extends AbstractDiffable<AutoscalingPolicy> imple
|
|||
public AutoscalingPolicy(final StreamInput in) throws IOException {
|
||||
name = in.readString();
|
||||
deciders = new TreeMap<>(
|
||||
in.readNamedWriteableList(AutoscalingDecider.class)
|
||||
in.readNamedWriteableList(AutoscalingDeciderConfiguration.class)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(AutoscalingDecider::name, Function.identity()))
|
||||
.collect(Collectors.toMap(AutoscalingDeciderConfiguration::name, Function.identity()))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -95,7 +97,7 @@ public class AutoscalingPolicy extends AbstractDiffable<AutoscalingPolicy> imple
|
|||
{
|
||||
builder.startObject(DECIDERS_FIELD.getPreferredName());
|
||||
{
|
||||
for (final Map.Entry<String, AutoscalingDecider> entry : deciders.entrySet()) {
|
||||
for (final Map.Entry<String, AutoscalingDeciderConfiguration> entry : deciders.entrySet()) {
|
||||
builder.field(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
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.AlwaysAutoscalingDeciderConfiguration;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDeciderConfiguration;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecision;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisionType;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisions;
|
||||
|
@ -68,11 +68,11 @@ public abstract class AutoscalingTestCase extends ESTestCase {
|
|||
return new AutoscalingDecisions(decisions);
|
||||
}
|
||||
|
||||
public static SortedMap<String, AutoscalingDecider> randomAutoscalingDeciders() {
|
||||
public static SortedMap<String, AutoscalingDeciderConfiguration> randomAutoscalingDeciders() {
|
||||
return new TreeMap<>(
|
||||
org.elasticsearch.common.collect.List.of(new AlwaysAutoscalingDecider())
|
||||
org.elasticsearch.common.collect.List.of(new AlwaysAutoscalingDeciderConfiguration())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(AutoscalingDecider::name, Function.identity()))
|
||||
.collect(Collectors.toMap(AutoscalingDeciderConfiguration::name, Function.identity()))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ public abstract class AutoscalingTestCase extends ESTestCase {
|
|||
}
|
||||
|
||||
public static AutoscalingPolicy mutateAutoscalingPolicy(final AutoscalingPolicy instance) {
|
||||
final SortedMap<String, AutoscalingDecider> deciders;
|
||||
final SortedMap<String, AutoscalingDeciderConfiguration> deciders;
|
||||
if (randomBoolean()) {
|
||||
// if the policy name did not change, or randomly, use a mutated set of deciders
|
||||
deciders = mutateAutoscalingDeciders(instance.deciders());
|
||||
|
@ -95,7 +95,9 @@ public abstract class AutoscalingTestCase extends ESTestCase {
|
|||
return new AutoscalingPolicy(randomValueOtherThan(instance.name(), () -> randomAlphaOfLength(8)), deciders);
|
||||
}
|
||||
|
||||
public static SortedMap<String, AutoscalingDecider> mutateAutoscalingDeciders(final SortedMap<String, AutoscalingDecider> deciders) {
|
||||
public static SortedMap<String, AutoscalingDeciderConfiguration> mutateAutoscalingDeciders(
|
||||
final SortedMap<String, AutoscalingDeciderConfiguration> deciders
|
||||
) {
|
||||
if (deciders.size() == 0) {
|
||||
return randomAutoscalingDeciders();
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase;
|
||||
import org.elasticsearch.xpack.autoscaling.decision.AutoscalingDecisions;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class GetAutoscalingDecisionActionResponseTests extends AutoscalingTestCase {
|
||||
|
||||
public void testToXContent() throws IOException {
|
||||
Set<String> policyNames = IntStream.range(0, randomIntBetween(1, 10))
|
||||
.mapToObj(i -> randomAlphaOfLength(10))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
SortedMap<String, AutoscalingDecisions> decisions = new TreeMap<>(
|
||||
policyNames.stream().map(s -> Tuple.tuple(s, randomAutoscalingDecisions())).collect(Collectors.toMap(Tuple::v1, Tuple::v2))
|
||||
);
|
||||
|
||||
GetAutoscalingDecisionAction.Response response = new GetAutoscalingDecisionAction.Response(decisions);
|
||||
XContentType xContentType = randomFrom(XContentType.values());
|
||||
|
||||
XContentBuilder builder = XContentBuilder.builder(xContentType.xContent());
|
||||
response.toXContent(builder, null);
|
||||
BytesReference responseBytes = BytesReference.bytes(builder);
|
||||
|
||||
XContentBuilder expected = XContentBuilder.builder(xContentType.xContent());
|
||||
expected.startObject();
|
||||
expected.startArray("decisions");
|
||||
for (Map.Entry<String, AutoscalingDecisions> entry : decisions.entrySet()) {
|
||||
expected.startObject();
|
||||
expected.field(entry.getKey(), entry.getValue());
|
||||
expected.endObject();
|
||||
}
|
||||
expected.endArray();
|
||||
expected.endObject();
|
||||
BytesReference expectedBytes = BytesReference.bytes(expected);
|
||||
assertThat(responseBytes, Matchers.equalTo(expectedBytes));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.Metadata;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
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 org.hamcrest.Matchers;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class AutoscalingDecisionServiceTests extends AutoscalingTestCase {
|
||||
public void testAlwaysDecision() {
|
||||
AutoscalingDecisionService service = new AutoscalingDecisionService(
|
||||
org.elasticsearch.common.collect.Set.of(new AlwaysAutoscalingDeciderService())
|
||||
);
|
||||
Set<String> policyNames = IntStream.range(0, randomIntBetween(1, 10))
|
||||
.mapToObj(i -> randomAlphaOfLength(10))
|
||||
.collect(Collectors.toSet());
|
||||
SortedMap<String, AutoscalingDeciderConfiguration> deciders = new TreeMap<>(
|
||||
org.elasticsearch.common.collect.Map.of(AlwaysAutoscalingDeciderConfiguration.NAME, new AlwaysAutoscalingDeciderConfiguration())
|
||||
);
|
||||
SortedMap<String, AutoscalingPolicyMetadata> policies = new TreeMap<>(
|
||||
policyNames.stream()
|
||||
.map(s -> Tuple.tuple(s, new AutoscalingPolicyMetadata(new AutoscalingPolicy(s, deciders))))
|
||||
.collect(Collectors.toMap(Tuple::v1, Tuple::v2))
|
||||
);
|
||||
ClusterState state = ClusterState.builder(ClusterName.DEFAULT)
|
||||
.metadata(Metadata.builder().putCustom(AutoscalingMetadata.NAME, new AutoscalingMetadata(policies)))
|
||||
.build();
|
||||
SortedMap<String, AutoscalingDecisions> decisions = service.decide(state);
|
||||
SortedMap<String, AutoscalingDecisions> expected = new TreeMap<>(
|
||||
policyNames.stream()
|
||||
.map(
|
||||
s -> Tuple.tuple(
|
||||
s,
|
||||
new AutoscalingDecisions(
|
||||
org.elasticsearch.common.collect.List.of(
|
||||
new AutoscalingDecision(
|
||||
AlwaysAutoscalingDeciderConfiguration.NAME,
|
||||
AutoscalingDecisionType.SCALE_UP,
|
||||
"always"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.collect(Collectors.toMap(Tuple::v1, Tuple::v2))
|
||||
);
|
||||
assertThat(decisions, Matchers.equalTo(expected));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue