Add autoscaling API skelton (#51564)

The main purpose of this commit is to add a single autoscaling REST
endpoint skeleton, for the purpose of starting to build out the build
and testing infrastructure that will surround it. For example, rather
than commiting a fully-functioning autoscaling API, we introduce here
the skeleton so that we can start wiring up the build and testing
infrastructure, establish security roles/permissions, an so on. This
way, in a forthcoming PR that introduces actual functionality, that PR
will be smaller and have less distractions around that sort of
infrastructure.
This commit is contained in:
Jason Tedor 2020-02-06 21:55:01 -05:00 committed by GitHub
parent 488944f4a1
commit 25daf5f1e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 443 additions and 6 deletions

View File

@ -318,6 +318,7 @@ public final class Role {
public static final String MANAGE_OIDC = "manage_oidc";
public static final String MANAGE_TOKEN = "manage_token";
public static final String MANAGE_PIPELINE = "manage_pipeline";
public static final String MANAGE_AUTOSCALING = "manage_autoscaling";
public static final String MANAGE_CCR = "manage_ccr";
public static final String READ_CCR = "read_ccr";
public static final String MANAGE_ILM = "manage_ilm";
@ -326,8 +327,8 @@ public final class Role {
public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_TRANSFORM_DEPRECATED, MONITOR_TRANSFORM,
MONITOR_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE, MANAGE_TRANSFORM_DEPRECATED, MANAGE_TRANSFORM,
MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT,
MANAGE_SECURITY, MANAGE_SAML, MANAGE_OIDC, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM,
MANAGE_ENRICH };
MANAGE_SECURITY, MANAGE_SAML, MANAGE_OIDC, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_AUTOSCALING, MANAGE_CCR, READ_CCR,
MANAGE_ILM, READ_ILM, MANAGE_ENRICH };
}
/**

View File

@ -0,0 +1,15 @@
[role="xpack"]
[testenv="platinum"]
[[autoscaling-apis]]
== Autoscaling APIs
You can use the following APIs to perform autoscaling operations.
[float]
[[autoscaling-api-top-level]]
=== Top-Level
* <<autoscaling-get-autoscaling-decision,Get autoscaling decision>>
// top-level
include::get-autoscaling-decision.asciidoc[]

View File

@ -0,0 +1,52 @@
[role="xpack"]
[testenv="platinum"]
[[autoscaling-get-autoscaling-decision]]
=== Get autoscaling decision API
++++
<titleabbrev>Get autoscaling decision</titleabbrev>
++++
Get autoscaling decision.
[[autoscaling-get-autoscaling-decision-request]]
==== {api-request-title}
[source,console]
--------------------------------------------------
GET /_autoscaling/decision/
--------------------------------------------------
// TEST
[[autoscaling-get-autoscaling-decision-prereqs]]
==== {api-prereq-title}
* If the {es} {security-features} are enabled, you must have
`manage_autoscaling` cluster privileges. For more information, see
<<security-privileges>>.
[[autoscaling-get-autoscaling-decision-desc]]
==== {api-description-title}
This API gets the current autoscaling decision based on the configured
autoscaling policy. This API will return whether or not autoscaling is
needed.
[[autoscaling-get-autoscaling-decision-examples]]
==== {api-examples-title}
This example retrieves the current autoscaling decision.
[source,console]
--------------------------------------------------
GET /_autoscaling/decision
--------------------------------------------------
// TEST
The API returns the following result:
[source,console-result]
--------------------------------------------------
{
}
--------------------------------------------------

View File

@ -35,6 +35,7 @@ not be included yet.
--
include::{es-repo-dir}/api-conventions.asciidoc[]
include::{es-repo-dir}/autoscaling/apis/autoscaling-apis.asciidoc[]
include::{es-repo-dir}/cat.asciidoc[]
include::{es-repo-dir}/cluster.asciidoc[]
include::{es-repo-dir}/ccr/apis/ccr-apis.asciidoc[]

View File

@ -66,6 +66,7 @@ A successful call returns an object with "cluster" and "index" fields.
"delegate_pki",
"manage",
"manage_api_key",
"manage_autoscaling",
"manage_ccr",
"manage_data_frame_transforms",
"manage_enrich",

View File

@ -18,3 +18,12 @@ dependencies {
compileOnly project(path: xpackModule('core'), configuration: 'default')
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
}
// add all sub-projects of the qa sub-project
gradle.projectsEvaluated {
project.subprojects
.find { it.path == project.path + ":qa" }
.subprojects
.findAll { it.path.startsWith(project.path + ":qa") }
.each { check.dependsOn it.check }
}

View File

@ -0,0 +1,18 @@
import org.elasticsearch.gradle.test.RestIntegTestTask
apply plugin: 'elasticsearch.build'
test.enabled = false
dependencies {
compile project(':test:framework')
}
subprojects {
project.tasks.withType(RestIntegTestTask) {
final File xPackResources = new File(xpackProject('plugin').projectDir, 'src/test/resources')
project.copyRestSpec.from(xPackResources) {
include 'rest-api-spec/api/**'
}
}
}

View File

@ -0,0 +1,3 @@
autoscaling:
cluster:
- manage_autoscaling

View File

@ -0,0 +1,25 @@
import org.elasticsearch.gradle.test.RestIntegTestTask
apply plugin: 'elasticsearch.testclusters'
apply plugin: 'elasticsearch.standalone-test'
dependencies {
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
testCompile project(path: xpackModule('autoscaling'), configuration: 'runtime')
}
task restTest(type: RestIntegTestTask) {
mustRunAfter(precommit)
}
testClusters.restTest {
testDistribution = 'DEFAULT'
setting 'xpack.autoscaling.enabled', 'true'
setting 'xpack.security.enabled', 'true'
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'
}
check.dependsOn restTest
test.enabled = false

View File

@ -0,0 +1,41 @@
/*
* 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 com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class AutoscalingRestIT extends ESClientYamlSuiteTestCase {
public AutoscalingRestIT(final ClientYamlTestCandidate testCandidate) {
super(testCandidate);
}
@ParametersFactory
public static Iterable<Object[]> parameters() throws Exception {
return ESClientYamlSuiteTestCase.createParameters();
}
@Override
protected Settings restAdminSettings() {
final String value = basicAuthHeaderValue("autoscaling-admin", new SecureString("autoscaling-admin-password".toCharArray()));
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", value).build();
}
@Override
protected Settings restClientSettings() {
final String value = basicAuthHeaderValue("autoscaling-user", new SecureString("autoscaling-user-password".toCharArray()));
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", value).build();
}
}

View File

@ -0,0 +1,4 @@
---
"Test get autoscaling decision":
- do:
autoscaling.get_autoscaling_decision: {}

View File

@ -7,16 +7,31 @@
package org.elasticsearch.xpack.autoscaling;
import org.elasticsearch.Build;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNodes;
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.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.TransportGetAutoscalingDecisionAction;
import org.elasticsearch.xpack.autoscaling.rest.RestGetAutoscalingDecisionHandler;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
/**
* Container class for autoscaling functionality.
*/
public class Autoscaling extends Plugin {
public class Autoscaling extends Plugin implements ActionPlugin {
public static final Setting<Boolean> AUTOSCALING_ENABLED_SETTING = Setting.boolSetting(
"xpack.autoscaling.enabled",
@ -24,6 +39,12 @@ public class Autoscaling extends Plugin {
Setting.Property.NodeScope
);
private final boolean enabled;
public Autoscaling(final Settings settings) {
this.enabled = AUTOSCALING_ENABLED_SETTING.get(settings);
}
/**
* The settings defined by autoscaling.
*
@ -38,6 +59,34 @@ public class Autoscaling extends Plugin {
}
}
@Override
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
if (enabled) {
return Collections.singletonList(
new ActionHandler<>(GetAutoscalingDecisionAction.INSTANCE, TransportGetAutoscalingDecisionAction.class)
);
} else {
return Collections.emptyList();
}
}
@Override
public List<RestHandler> getRestHandlers(
final Settings settings,
final RestController controller,
final ClusterSettings clusterSettings,
final IndexScopedSettings indexScopedSettings,
final SettingsFilter settingsFilter,
final IndexNameExpressionResolver indexNameExpressionResolver,
final Supplier<DiscoveryNodes> nodesInCluster
) {
if (enabled) {
return Collections.singletonList(new RestGetAutoscalingDecisionHandler(controller));
} else {
return Collections.emptyList();
}
}
boolean isSnapshot() {
return Build.CURRENT.isSnapshot();
}

View File

@ -0,0 +1,88 @@
/*
* 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.ActionResponse;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
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 java.io.IOException;
public class GetAutoscalingDecisionAction extends ActionType<GetAutoscalingDecisionAction.Response> {
public static final GetAutoscalingDecisionAction INSTANCE = new GetAutoscalingDecisionAction();
public static final String NAME = "cluster:admin/autoscaling/get_autoscaling_decision";
private GetAutoscalingDecisionAction() {
super(NAME, Response::new);
}
public static class Request extends AcknowledgedRequest<GetAutoscalingDecisionAction.Request> implements ToXContentObject {
public Request() {
}
public Request(final StreamInput in) throws IOException {
super(in);
}
@Override
public void writeTo(final StreamOutput out) throws IOException {
super.writeTo(out);
}
@Override
public ActionRequestValidationException validate() {
return null;
}
@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject();
{
}
builder.endObject();
return builder;
}
}
public static class Response extends ActionResponse implements ToXContentObject {
public Response() {
}
public Response(final StreamInput in) throws IOException {
super(in);
}
@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;
}
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
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 java.io.IOException;
public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAction<
GetAutoscalingDecisionAction.Request,
GetAutoscalingDecisionAction.Response> {
@Inject
public TransportGetAutoscalingDecisionAction(
final TransportService transportService,
final ClusterService clusterService,
final ThreadPool threadPool,
final ActionFilters actionFilters,
final IndexNameExpressionResolver indexNameExpressionResolver
) {
super(
GetAutoscalingDecisionAction.NAME,
transportService,
clusterService,
threadPool,
actionFilters,
GetAutoscalingDecisionAction.Request::new,
indexNameExpressionResolver
);
}
@Override
protected String executor() {
return ThreadPool.Names.SAME;
}
@Override
protected GetAutoscalingDecisionAction.Response read(final StreamInput in) throws IOException {
return new GetAutoscalingDecisionAction.Response(in);
}
@Override
protected void masterOperation(
final GetAutoscalingDecisionAction.Request request,
final ClusterState state,
final ActionListener<GetAutoscalingDecisionAction.Response> listener
) {
listener.onResponse(new GetAutoscalingDecisionAction.Response());
}
@Override
protected ClusterBlockException checkBlock(final GetAutoscalingDecisionAction.Request request, final ClusterState state) {
return null;
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingDecisionAction;
public class RestGetAutoscalingDecisionHandler extends BaseRestHandler {
public RestGetAutoscalingDecisionHandler(final RestController controller) {
controller.registerHandler(RestRequest.Method.GET, "/_autoscaling/decision", this);
}
@Override
public String getName() {
return "get_autoscaling_decision";
}
@Override
protected RestChannelConsumer prepareRequest(final RestRequest restRequest, final NodeClient client) {
final GetAutoscalingDecisionAction.Request request = new GetAutoscalingDecisionAction.Request();
return channel -> client.execute(GetAutoscalingDecisionAction.INSTANCE, request, new RestToXContentListener<>(channel));
}
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.autoscaling;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.hasItem;
@ -14,7 +15,7 @@ import static org.hamcrest.Matchers.not;
public class AutoscalingTests extends ESTestCase {
public void testEnabledSettingRegisteredInSnapshotBuilds() {
final Autoscaling plugin = new Autoscaling() {
final Autoscaling plugin = new Autoscaling(Settings.EMPTY) {
@Override
protected boolean isSnapshot() {
@ -26,7 +27,7 @@ public class AutoscalingTests extends ESTestCase {
}
public void testEnabledSettingNotRegisteredInNonSnapshotBuilds() {
final Autoscaling plugin = new Autoscaling() {
final Autoscaling plugin = new Autoscaling(Settings.EMPTY) {
@Override
protected boolean isSnapshot() {

View File

@ -120,6 +120,9 @@ public class ClusterPrivilegeResolver {
public static final NamedClusterPrivilege MANAGE_API_KEY = new ActionClusterPrivilege("manage_api_key", MANAGE_API_KEY_PATTERN);
public static final NamedClusterPrivilege MANAGE_PIPELINE = new ActionClusterPrivilege("manage_pipeline",
Collections.singleton("cluster:admin/ingest/pipeline/*"));
public static final NamedClusterPrivilege MANAGE_AUTOSCALING = new ActionClusterPrivilege(
"manage_autoscaling",
Collections.singleton("cluster:admin/autoscaling/*"));
public static final NamedClusterPrivilege MANAGE_CCR = new ActionClusterPrivilege("manage_ccr", MANAGE_CCR_PATTERN);
public static final NamedClusterPrivilege READ_CCR = new ActionClusterPrivilege("read_ccr", READ_CCR_PATTERN);
public static final NamedClusterPrivilege CREATE_SNAPSHOT = new ActionClusterPrivilege("create_snapshot", CREATE_SNAPSHOT_PATTERN);
@ -159,6 +162,7 @@ public class ClusterPrivilegeResolver {
MANAGE_API_KEY,
MANAGE_PIPELINE,
MANAGE_ROLLUP,
MANAGE_AUTOSCALING,
MANAGE_CCR,
READ_CCR,
CREATE_SNAPSHOT,

View File

@ -174,6 +174,10 @@ public class PrivilegeTests extends ESTestCase {
assertThat(predicate.test("indices:admin/settings/foo"), is(false));
}
public void testManageAutoscalingPrivilege() {
verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_AUTOSCALING, "cluster:admin/autoscaling/get_decision");
}
public void testManageCcrPrivilege() {
verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_CCR, "cluster:admin/xpack/ccr/follow_index",
"cluster:admin/xpack/ccr/unfollow_index", "cluster:admin/xpack/ccr/brand_new_api");

View File

@ -0,0 +1,18 @@
{
"autoscaling.get_autoscaling_decision":{
"documentation":{
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-get-autoscaling-decision.html"
},
"stability":"experimental",
"url":{
"paths":[
{
"path":"/_autoscaling/decision",
"methods":[
"GET"
]
}
]
}
}
}

View File

@ -15,5 +15,5 @@ setup:
# This is fragile - it needs to be updated every time we add a new cluster/index privilege
# I would much prefer we could just check that specific entries are in the array, but we don't have
# an assertion for that
- length: { "cluster" : 34 }
- length: { "cluster" : 35 }
- length: { "index" : 18 }