Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add license checks for autoscaling #66235

Merged
merged 8 commits into from
Dec 15, 2020
1 change: 1 addition & 0 deletions x-pack/plugin/autoscaling/qa/rest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ testClusters.all {
}
setting 'xpack.autoscaling.enabled', 'true'
setting 'xpack.security.enabled', 'true'
setting 'xpack.license.self_generated.type', 'trial'
extraConfigFile 'roles.yml', file('autoscaling-roles.yml')
user username: 'autoscaling-admin', password: 'autoscaling-admin-password', role: 'superuser'
user username: 'autoscaling-user', password: 'autoscaling-user-password', role: 'autoscaling'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESSingleNodeTestCase;
import org.elasticsearch.xpack.autoscaling.action.DeleteAutoscalingPolicyAction;
import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingCapacityAction;
import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingPolicyAction;
import org.elasticsearch.xpack.autoscaling.action.PutAutoscalingPolicyAction;
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;

import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;

public class AutoscalingLicenseCheckerIT extends ESSingleNodeTestCase {

public static class NonCompliantLicenseLocalStateAutoscaling extends LocalStateCompositeXPackPlugin {

public NonCompliantLicenseLocalStateAutoscaling(final Settings settings, final Path configPath) {
super(settings, configPath);
plugins.add(new Autoscaling(settings, new AutoscalingLicenseChecker(() -> false)));
}

}

@Override
protected Collection<Class<? extends Plugin>> getPlugins() {
return Collections.singletonList(NonCompliantLicenseLocalStateAutoscaling.class);
}

public void testCanNotPutPolicyWithNonCompliantLicense() throws InterruptedException {
final PutAutoscalingPolicyAction.Request request = new PutAutoscalingPolicyAction.Request(
"",
Collections.emptySortedSet(),
Collections.emptySortedMap()
);
final CountDownLatch latch = new CountDownLatch(1);
client().execute(PutAutoscalingPolicyAction.INSTANCE, request, new ActionListener<>() {

@Override
public void onResponse(final AcknowledgedResponse response) {
try {
fail();
} finally {
latch.countDown();
}
}

@Override
public void onFailure(final Exception e) {
try {
assertNonCompliantLicense(e);
} finally {
latch.countDown();
}
}

});
latch.await();
}

public void testCanNotGetPolicyWithNonCompliantLicense() throws InterruptedException {
final GetAutoscalingPolicyAction.Request request = new GetAutoscalingPolicyAction.Request("");
final CountDownLatch latch = new CountDownLatch(1);
client().execute(GetAutoscalingPolicyAction.INSTANCE, request, new ActionListener<>() {

@Override
public void onResponse(final GetAutoscalingPolicyAction.Response response) {
try {
fail();
} finally {
latch.countDown();
}
}

@Override
public void onFailure(final Exception e) {
try {
assertNonCompliantLicense(e);
} finally {
latch.countDown();
}
}

});
latch.await();
}

public void testCanNonGetAutoscalingCapacityDecisionWithNonCompliantLicense() throws InterruptedException {
final GetAutoscalingCapacityAction.Request request = new GetAutoscalingCapacityAction.Request();
final CountDownLatch latch = new CountDownLatch(1);
client().execute(GetAutoscalingCapacityAction.INSTANCE, request, new ActionListener<>() {

@Override
public void onResponse(final GetAutoscalingCapacityAction.Response response) {
try {
fail();
} finally {
latch.countDown();
}
}

@Override
public void onFailure(final Exception e) {
try {
assertNonCompliantLicense(e);
} finally {
latch.countDown();
}
}

});
latch.await();
}

public void testCanDeleteAutoscalingPolicyEvenWithNonCompliantLicense() throws InterruptedException {
final DeleteAutoscalingPolicyAction.Request request = new DeleteAutoscalingPolicyAction.Request("*");
final CountDownLatch latch = new CountDownLatch(1);
client().execute(DeleteAutoscalingPolicyAction.INSTANCE, request, new ActionListener<>() {

@Override
public void onResponse(final AcknowledgedResponse response) {
latch.countDown();
}

@Override
public void onFailure(final Exception e) {
try {
fail();
} finally {
latch.countDown();
}
}

});
latch.await();
}

private void assertNonCompliantLicense(final Exception e) {
assertThat(e, instanceOf(ElasticsearchSecurityException.class));
assertThat(e.getMessage(), equalTo("current license is non-compliant for [autoscaling]"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
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;
Expand All @@ -51,11 +50,11 @@
import org.elasticsearch.xpack.autoscaling.rest.RestGetAutoscalingCapacityHandler;
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.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -92,12 +91,17 @@ public class Autoscaling extends Plugin implements ActionPlugin, ExtensiblePlugi
);

private final boolean enabled;

private final List<AutoscalingExtension> autoscalingExtensions;
private final AutoscalingLicenseChecker autoscalingLicenseChecker;

public Autoscaling(final Settings settings) {
this(settings, new AutoscalingLicenseChecker());
}

Autoscaling(final Settings settings, final AutoscalingLicenseChecker autoscalingLicenseChecker) {
this.enabled = AUTOSCALING_ENABLED_SETTING.get(settings);
this.autoscalingExtensions = new ArrayList<>(List.of(this));
this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker);
}

/**
Expand Down Expand Up @@ -132,7 +136,7 @@ public Collection<Object> createComponents(
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<RepositoriesService> repositoriesServiceSupplier
) {
return List.of(new AutoscalingCalculateCapacityService.Holder(this));
return List.of(new AutoscalingCalculateCapacityService.Holder(this), autoscalingLicenseChecker);
}

@Override
Expand Down Expand Up @@ -191,10 +195,6 @@ public List<NamedXContentRegistry.Entry> getNamedXContent() {
);
}

protected XPackLicenseState getLicenseState() {
return XPackPlugin.getSharedLicenseState();
}

@Override
public void loadExtensions(ExtensionLoader loader) {
loader.loadExtensions(AutoscalingExtension.class).forEach(autoscalingExtensions::add);
Expand All @@ -208,4 +208,5 @@ public Collection<AutoscalingDeciderService> deciders() {
public Set<AutoscalingDeciderService> createDeciderServices() {
return autoscalingExtensions.stream().flatMap(p -> p.deciders().stream()).collect(Collectors.toSet());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.license.XPackLicenseState;
import org.elasticsearch.xpack.core.XPackPlugin;

import java.util.Objects;
import java.util.function.BooleanSupplier;

/**
* Encapsulates license checking for autoscaling.
*/
public class AutoscalingLicenseChecker {

private final BooleanSupplier isAutoscalingAllowed;

/**
* Constructs an autoscaling license checker with the default rule based on the license state for checking if autoscaling is allowed.
*/
AutoscalingLicenseChecker() {
this(() -> XPackPlugin.getSharedLicenseState().checkFeature(XPackLicenseState.Feature.AUTOSCALING));
}

/**
* Constructs an autoscaling license checker with the specified boolean supplier.
*
* @param isAutoscalingAllowed a boolean supplier that should return true is autoscaling is allowed and otherwise false.
*/
public AutoscalingLicenseChecker(final BooleanSupplier isAutoscalingAllowed) {
this.isAutoscalingAllowed = Objects.requireNonNull(isAutoscalingAllowed);
}

/**
* Returns whether or not autoscaling is allowed.
*
* @return true if autoscaling is allowed, otherwise false
*/
public boolean isAutoscalingAllowed() {
return isAutoscalingAllowed.getAsBoolean();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ protected void masterOperation(
final ClusterState state,
final ActionListener<AcknowledgedResponse> listener
) {
// no license check, we will allow deleting policies even if the license is out of compliance, for cleanup purposes

clusterService.submitStateUpdateTask("delete-autoscaling-policy", new AckedClusterStateUpdateTask(request, listener) {
@Override
public ClusterState execute(final ClusterState currentState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,22 @@
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.autoscaling.AutoscalingLicenseChecker;
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingCalculateCapacityService;

import java.util.Objects;

public class TransportGetAutoscalingCapacityAction extends TransportMasterNodeAction<
GetAutoscalingCapacityAction.Request,
GetAutoscalingCapacityAction.Response> {

private final AutoscalingCalculateCapacityService capacityService;
private final ClusterInfoService clusterInfoService;
private final AutoscalingLicenseChecker autoscalingLicenseChecker;

@Inject
public TransportGetAutoscalingCapacityAction(
Expand All @@ -35,7 +40,8 @@ public TransportGetAutoscalingCapacityAction(
final ActionFilters actionFilters,
final IndexNameExpressionResolver indexNameExpressionResolver,
final AutoscalingCalculateCapacityService.Holder capacityServiceHolder,
final ClusterInfoService clusterInfoService
final ClusterInfoService clusterInfoService,
final AutoscalingLicenseChecker autoscalingLicenseChecker
) {
super(
GetAutoscalingCapacityAction.NAME,
Expand All @@ -50,6 +56,7 @@ public TransportGetAutoscalingCapacityAction(
);
this.capacityService = capacityServiceHolder.get();
this.clusterInfoService = clusterInfoService;
this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker);
assert this.capacityService != null;
}

Expand All @@ -60,6 +67,11 @@ protected void masterOperation(
final ClusterState state,
final ActionListener<GetAutoscalingCapacityAction.Response> listener
) {
if (autoscalingLicenseChecker.isAutoscalingAllowed() == false) {
listener.onFailure(LicenseUtils.newComplianceException("autoscaling"));
return;
}

listener.onResponse(
new GetAutoscalingCapacityAction.Response(capacityService.calculate(state, clusterInfoService.getClusterInfo()))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,30 @@
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.autoscaling.AutoscalingLicenseChecker;
import org.elasticsearch.xpack.autoscaling.AutoscalingMetadata;
import org.elasticsearch.xpack.autoscaling.policy.AutoscalingPolicy;

import java.util.Objects;

public class TransportGetAutoscalingPolicyAction extends TransportMasterNodeAction<
GetAutoscalingPolicyAction.Request,
GetAutoscalingPolicyAction.Response> {

private final AutoscalingLicenseChecker autoscalingLicenseChecker;

@Inject
public TransportGetAutoscalingPolicyAction(
final TransportService transportService,
final ClusterService clusterService,
final ThreadPool threadPool,
final ActionFilters actionFilters,
final IndexNameExpressionResolver indexNameExpressionResolver
final IndexNameExpressionResolver indexNameExpressionResolver,
final AutoscalingLicenseChecker autoscalingLicenseChecker
) {
super(
GetAutoscalingPolicyAction.NAME,
Expand All @@ -45,6 +52,7 @@ public TransportGetAutoscalingPolicyAction(
GetAutoscalingPolicyAction.Response::new,
ThreadPool.Names.SAME
);
this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker);
}

@Override
Expand All @@ -54,6 +62,11 @@ protected void masterOperation(
final ClusterState state,
final ActionListener<GetAutoscalingPolicyAction.Response> listener
) {
if (autoscalingLicenseChecker.isAutoscalingAllowed() == false) {
listener.onFailure(LicenseUtils.newComplianceException("autoscaling"));
return;
}

listener.onResponse(new GetAutoscalingPolicyAction.Response(getAutoscalingPolicy(state, request.name())));
}

Expand Down
Loading