Skip to content

Commit

Permalink
Add license checks for autoscaling (#66235)
Browse files Browse the repository at this point in the history
This commit enables license checks for autoscaling. Autoscaling will be
licensed under at the Enterprise license tier. All REST actions except
for deleting autoscaling policies when a license is out of compliance.
  • Loading branch information
jasontedor committed Dec 15, 2020
1 parent 41ad305 commit b8c55d6
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 26 deletions.
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 @@ -16,6 +16,7 @@ restResources {
testClusters.all {
testDistribution = 'DEFAULT'
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(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<AcknowledgedResponse>() {

@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<GetAutoscalingPolicyAction.Response>() {

@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<GetAutoscalingCapacityAction.Response>() {

@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<AcknowledgedResponse>() {

@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 @@ -52,11 +51,11 @@
import org.elasticsearch.xpack.autoscaling.rest.RestGetAutoscalingPolicyHandler;
import org.elasticsearch.xpack.autoscaling.rest.RestPutAutoscalingPolicyHandler;
import org.elasticsearch.xpack.autoscaling.storage.ReactiveStorageDeciderService;
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 All @@ -76,9 +75,15 @@ public class Autoscaling extends Plugin implements ActionPlugin, ExtensiblePlugi
private final List<AutoscalingExtension> autoscalingExtensions;
private final SetOnce<ClusterService> clusterService = new SetOnce<>();
private final SetOnce<AllocationDeciders> allocationDeciders = new SetOnce<>();
private final AutoscalingLicenseChecker autoscalingLicenseChecker;

public Autoscaling() {
this(new AutoscalingLicenseChecker());
}

Autoscaling(final AutoscalingLicenseChecker autoscalingLicenseChecker) {
this.autoscalingExtensions = new ArrayList<>(org.elasticsearch.common.collect.List.of(this));
this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker);
}

@Override
Expand All @@ -96,7 +101,7 @@ public Collection<Object> createComponents(
Supplier<RepositoriesService> repositoriesServiceSupplier
) {
this.clusterService.set(clusterService);
return org.elasticsearch.common.collect.List.of(new AutoscalingCalculateCapacityService.Holder(this));
return org.elasticsearch.common.collect.List.of(new AutoscalingCalculateCapacityService.Holder(this), autoscalingLicenseChecker);
}

@Override
Expand Down Expand Up @@ -152,10 +157,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 @@ -178,4 +179,5 @@ public Set<AutoscalingDeciderService> createDeciderServices(AllocationDeciders a
this.allocationDeciders.set(allocationDeciders);
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 @@ -60,6 +60,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 @@ -16,18 +16,23 @@
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.snapshots.SnapshotsInfoService;
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 SnapshotsInfoService snapshotsInfoService;
private final AutoscalingLicenseChecker autoscalingLicenseChecker;

@Inject
public TransportGetAutoscalingCapacityAction(
Expand All @@ -39,7 +44,8 @@ public TransportGetAutoscalingCapacityAction(
final AutoscalingCalculateCapacityService.Holder capacityServiceHolder,
final ClusterInfoService clusterInfoService,
final SnapshotsInfoService snapshotsInfoService,
final AllocationDeciders allocationDeciders
final AllocationDeciders allocationDeciders,
final AutoscalingLicenseChecker autoscalingLicenseChecker
) {
super(
GetAutoscalingCapacityAction.NAME,
Expand All @@ -55,6 +61,7 @@ public TransportGetAutoscalingCapacityAction(
this.snapshotsInfoService = snapshotsInfoService;
this.capacityService = capacityServiceHolder.get(allocationDeciders);
this.clusterInfoService = clusterInfoService;
this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker);
assert this.capacityService != null;
}

Expand All @@ -64,6 +71,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(), snapshotsInfoService.snapshotShardSizes())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,29 @@
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.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 @@ -44,6 +51,7 @@ public TransportGetAutoscalingPolicyAction(
GetAutoscalingPolicyAction.Response::new,
ThreadPool.Names.SAME
);
this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker);
}

@Override
Expand All @@ -52,6 +60,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

0 comments on commit b8c55d6

Please sign in to comment.