Add Upgrade API Index Upgrade Info action (elastic/x-pack-elasticsearch#1264)

Adds a new Upgrade API with the first action, index upgrade info, that returns that list of indices that require upgrade in the current cluster before the cluster can be upgraded to the next major version.

Relates to elastic/x-pack-elasticsearch#1214

Original commit: elastic/x-pack-elasticsearch@761e7d2128
This commit is contained in:
Igor Motov 2017-05-11 10:03:12 -04:00
parent 84574c0367
commit bb034f42b8
17 changed files with 1109 additions and 4 deletions

View File

@ -100,6 +100,8 @@ import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.ssl.SSLConfigurationReloader; import org.elasticsearch.xpack.ssl.SSLConfigurationReloader;
import org.elasticsearch.xpack.ssl.SSLService; import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.upgrade.Upgrade;
import org.elasticsearch.xpack.upgrade.InternalIndexUpgradeCheck;
import org.elasticsearch.xpack.watcher.Watcher; import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.watcher.WatcherFeatureSet; import org.elasticsearch.xpack.watcher.WatcherFeatureSet;
@ -108,11 +110,7 @@ import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.AccessController; import java.security.AccessController;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Clock; import java.time.Clock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -203,6 +201,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
protected MachineLearning machineLearning; protected MachineLearning machineLearning;
protected Logstash logstash; protected Logstash logstash;
protected Deprecation deprecation; protected Deprecation deprecation;
protected Upgrade upgrade;
public XPackPlugin( public XPackPlugin(
final Settings settings, final Settings settings,
@ -221,6 +220,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
this.machineLearning = new MachineLearning(settings, env, licenseState); this.machineLearning = new MachineLearning(settings, env, licenseState);
this.logstash = new Logstash(settings); this.logstash = new Logstash(settings);
this.deprecation = new Deprecation(); this.deprecation = new Deprecation();
this.upgrade = new Upgrade(settings, Collections.singletonList(new InternalIndexUpgradeCheck()));
// Check if the node is a transport client. // Check if the node is a transport client.
if (transportClientMode == false) { if (transportClientMode == false) {
this.extensionsService = new XPackExtensionsService(settings, resolveXPackExtensionsFile(env), getExtensions()); this.extensionsService = new XPackExtensionsService(settings, resolveXPackExtensionsFile(env), getExtensions());
@ -301,6 +301,9 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
components.addAll(logstash.createComponents(internalClient, clusterService)); components.addAll(logstash.createComponents(internalClient, clusterService));
components.addAll(upgrade.createComponents(internalClient, clusterService, threadPool, resourceWatcherService,
scriptService, xContentRegistry));
// just create the reloader as it will pull all of the loaded ssl configurations and start watching them // just create the reloader as it will pull all of the loaded ssl configurations and start watching them
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService); new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService);
return components; return components;
@ -425,6 +428,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
actions.addAll(graph.getActions()); actions.addAll(graph.getActions());
actions.addAll(machineLearning.getActions()); actions.addAll(machineLearning.getActions());
actions.addAll(deprecation.getActions()); actions.addAll(deprecation.getActions());
actions.addAll(upgrade.getActions());
return actions; return actions;
} }
@ -436,6 +440,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
filters.addAll(security.getActionFilters()); filters.addAll(security.getActionFilters());
filters.addAll(watcher.getActionFilters()); filters.addAll(watcher.getActionFilters());
filters.addAll(machineLearning.getActionFilters()); filters.addAll(machineLearning.getActionFilters());
filters.addAll(upgrade.getActionFilters());
return filters; return filters;
} }
@ -460,6 +465,8 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
indexNameExpressionResolver, nodesInCluster)); indexNameExpressionResolver, nodesInCluster));
handlers.addAll(deprecation.getRestHandlers(settings, restController, clusterSettings, indexScopedSettings, settingsFilter, handlers.addAll(deprecation.getRestHandlers(settings, restController, clusterSettings, indexScopedSettings, settingsFilter,
indexNameExpressionResolver, nodesInCluster)); indexNameExpressionResolver, nodesInCluster));
handlers.addAll(upgrade.getRestHandlers(settings, restController, clusterSettings, indexScopedSettings, settingsFilter,
indexNameExpressionResolver, nodesInCluster));
return handlers; return handlers;
} }

View File

@ -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.upgrade;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import java.util.Map;
/**
* Generic upgrade check applicable to all indices to be upgraded from the current version
* to the next major version
*/
public class GenericIndexUpgradeCheck implements IndexUpgradeCheck {
@Override
public String getName() {
return "generic";
}
@Override
public UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state) {
if (indexMetaData.getCreationVersion().before(Version.V_5_0_0_alpha1)) {
return UpgradeActionRequired.REINDEX;
}
return UpgradeActionRequired.UP_TO_DATE;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.upgrade;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/**
* Interface that for an index check and upgrade process
*/
public interface IndexUpgradeCheck {
String getName();
UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state);
default Collection<String> supportedParams() {
return Collections.emptyList();
}
}

View File

@ -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.upgrade;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class IndexUpgradeService extends AbstractComponent {
private final List<IndexUpgradeCheck> upgradeChecks;
private final IndexNameExpressionResolver indexNameExpressionResolver;
public IndexUpgradeService(Settings settings, List<IndexUpgradeCheck> upgradeChecks) {
super(settings);
this.upgradeChecks = upgradeChecks;
this.indexNameExpressionResolver = new IndexNameExpressionResolver(settings);
}
/**
* Returns the information about required upgrade action for the given indices
*
* @param indices list of indices to check, specify _all for all indices
* @param options wild card resolution option
* @param params list of additional parameters that will be passed to upgrade checks
* @param state the current cluster state
* @return a list of indices that should be upgraded/reindexed
*/
public Map<String, UpgradeActionRequired> upgradeInfo(String[] indices, IndicesOptions options, Map<String, String> params,
ClusterState state) {
Map<String, UpgradeActionRequired> results = new HashMap<>();
String[] concreteIndexNames = indexNameExpressionResolver.concreteIndexNames(state, options, indices);
MetaData metaData = state.getMetaData();
for (String index : concreteIndexNames) {
IndexMetaData indexMetaData = metaData.index(index);
indexCheck:
for (IndexUpgradeCheck check : upgradeChecks) {
UpgradeActionRequired upgradeActionRequired = check.actionRequired(indexMetaData, params, state);
logger.trace("[{}] check [{}] returned [{}]", index, check.getName(), upgradeActionRequired);
switch (upgradeActionRequired) {
case UPGRADE:
case REINDEX:
// this index needs to be upgraded or reindexed - skipping all other checks
results.put(index, upgradeActionRequired);
break indexCheck;
case UP_TO_DATE:
// this index is good - skipping all other checks
break indexCheck;
case NOT_APPLICABLE:
// this action is not applicable to this index - skipping to the next one
break;
default:
throw new IllegalStateException("unknown upgrade action " + upgradeActionRequired + " for the index "
+ index);
}
}
}
return results;
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.upgrade;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.tasks.TaskResultsService;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.watcher.watch.Watch;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Generic upgrade check applicable to all indices to be upgraded from the current version
* to the next major version
*/
public class InternalIndexUpgradeCheck implements IndexUpgradeCheck {
private final Set<String> KNOWN_INTERNAL_INDICES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Watch.INDEX,
SecurityLifecycleService.SECURITY_INDEX_NAME,
TaskResultsService.TASK_INDEX
)));
@Override
public String getName() {
return "inner";
}
@Override
public UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state) {
String indexName = indexMetaData.getIndex().getName();
if (KNOWN_INTERNAL_INDICES.contains(indexName)) {
return UpgradeActionRequired.UPGRADE;
}
if (isKibanaIndex(params.getOrDefault("kibana_indices", ".kibana"), indexName)) {
return UpgradeActionRequired.UPGRADE;
}
return UpgradeActionRequired.NOT_APPLICABLE;
}
private boolean isKibanaIndex(String kibanaIndicesMasks, String indexName) {
String[] kibanaIndices = Strings.delimitedListToStringArray(kibanaIndicesMasks, ",");
return Regex.simpleMatch(kibanaIndices, indexName);
}
@Override
public Collection<String> supportedParams() {
return Collections.singletonList("kibana_indices");
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.upgrade;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.IndexScopedSettings;
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.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.security.InternalClient;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction;
import org.elasticsearch.xpack.upgrade.rest.RestIndexUpgradeInfoAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class Upgrade implements ActionPlugin {
private Settings settings;
private List<IndexUpgradeCheck> customUpgradeChecks;
private Set<String> extraParameters;
public Upgrade(Settings settings, List<IndexUpgradeCheck> customUpgradeChecks) {
this.settings = settings;
this.customUpgradeChecks = customUpgradeChecks;
this.extraParameters = new HashSet<>();
for (IndexUpgradeCheck check : customUpgradeChecks) {
extraParameters.addAll(check.supportedParams());
}
}
public Collection<Object> createComponents(InternalClient internalClient, ClusterService clusterService, ThreadPool threadPool,
ResourceWatcherService resourceWatcherService, ScriptService scriptService,
NamedXContentRegistry xContentRegistry) {
List<IndexUpgradeCheck> upgradeChecks = new ArrayList<>(customUpgradeChecks);
// The generic test goes to the end of the list
upgradeChecks.add(new GenericIndexUpgradeCheck());
return Collections.singletonList(new IndexUpgradeService(settings, upgradeChecks));
}
@Override
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
return Collections.singletonList(
new ActionHandler<>(IndexUpgradeInfoAction.INSTANCE, IndexUpgradeInfoAction.TransportAction.class));
}
@Override
public List<RestHandler> getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings,
IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter,
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<DiscoveryNodes> nodesInCluster) {
return Collections.singletonList(
new RestIndexUpgradeInfoAction(settings, restController, extraParameters)
);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.upgrade;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import java.io.IOException;
import java.util.Locale;
/**
* Indicates the type of the upgrade required for the index
*/
public enum UpgradeActionRequired implements Writeable {
NOT_APPLICABLE, // Indicates that the check is not applicable to this index type, the next check will be performed
UP_TO_DATE, // Indicates that the check finds this index to be up to date - no additional checks are required
REINDEX, // The index should be reindex
UPGRADE; // The index should go through the upgrade procedure
public static UpgradeActionRequired fromString(String value) {
return UpgradeActionRequired.valueOf(value.toUpperCase(Locale.ROOT));
}
public static UpgradeActionRequired readFromStream(StreamInput in) throws IOException {
return in.readEnum(UpgradeActionRequired.class);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeEnum(this);
}
@Override
public String toString() {
return name().toLowerCase(Locale.ROOT);
}
}

View File

@ -0,0 +1,271 @@
/*
* 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.upgrade.actions;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder;
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
import org.elasticsearch.client.ElasticsearchClient;
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.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.upgrade.IndexUpgradeService;
import org.elasticsearch.xpack.upgrade.UpgradeActionRequired;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
public class IndexUpgradeInfoAction extends Action<IndexUpgradeInfoAction.Request, IndexUpgradeInfoAction.Response,
IndexUpgradeInfoAction.RequestBuilder> {
public static final IndexUpgradeInfoAction INSTANCE = new IndexUpgradeInfoAction();
public static final String NAME = "cluster:admin/xpack/upgrade/info";
private IndexUpgradeInfoAction() {
super(NAME);
}
@Override
public RequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new RequestBuilder(client, this);
}
@Override
public Response newResponse() {
return new Response();
}
public static class Response extends ActionResponse implements ToXContentObject {
private Map<String, UpgradeActionRequired> actions;
protected Response() {
}
public Response(Map<String, UpgradeActionRequired> actions) {
this.actions = actions;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
actions = in.readMap(StreamInput::readString, UpgradeActionRequired::readFromStream);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeMap(actions, StreamOutput::writeString, (out1, value) -> value.writeTo(out1));
}
public Map<String, UpgradeActionRequired> getActions() {
return actions;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
{
builder.startObject("indices");
for (Map.Entry<String, UpgradeActionRequired> entry : actions.entrySet()) {
builder.startObject(entry.getKey());
{
builder.field("action_required", entry.getValue().toString());
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
return builder;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Response response = (Response) o;
return Objects.equals(actions, response.actions);
}
@Override
public int hashCode() {
return Objects.hash(actions);
}
}
public static class Request extends MasterNodeReadRequest<Request> implements IndicesRequest.Replaceable {
private String[] indices = null;
private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, true, true);
private Map<String, String> extraParams = Collections.emptyMap();
// for serialization
public Request() {
}
public Request(String... indices) {
this.indices = indices;
}
@Override
public String[] indices() {
return indices;
}
@Override
public Request indices(String... indices) {
this.indices = indices;
return this;
}
@Override
public IndicesOptions indicesOptions() {
return indicesOptions;
}
public void indicesOptions(IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
}
public Map<String, String> extraParams() {
return extraParams;
}
public Request extraParams(Map<String, String> extraParams) {
this.extraParams = extraParams;
return this;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (indices == null) {
validationException = addValidationError("index/indices is missing", validationException);
}
if (extraParams == null) {
validationException = addValidationError("params are missing", validationException);
}
return validationException;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
indices = in.readStringArray();
indicesOptions = IndicesOptions.readIndicesOptions(in);
extraParams = in.readMap(StreamInput::readString, StreamInput::readString);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeStringArray(indices);
indicesOptions.writeIndicesOptions(out);
out.writeMap(extraParams, StreamOutput::writeString, StreamOutput::writeString);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Request request = (Request) o;
return Arrays.equals(indices, request.indices) &&
Objects.equals(indicesOptions.toString(), request.indicesOptions.toString()) &&
Objects.equals(extraParams, request.extraParams);
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(indices), indicesOptions.toString(), extraParams);
}
}
public static class RequestBuilder extends MasterNodeReadOperationRequestBuilder<Request, Response, RequestBuilder> {
protected RequestBuilder(ElasticsearchClient client, IndexUpgradeInfoAction action) {
super(client, action, new Request());
}
public RequestBuilder setIndices(String... indices) {
request.indices(indices);
return this;
}
public RequestBuilder setIndicesOptions(IndicesOptions indicesOptions) {
request.indicesOptions(indicesOptions);
return this;
}
public RequestBuilder setExtraParams(Map<String, String> params) {
request.extraParams(params);
return this;
}
}
public static class TransportAction extends TransportMasterNodeReadAction<Request, Response> {
private final IndexUpgradeService indexUpgradeService;
@Inject
public TransportAction(Settings settings, TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, ActionFilters actionFilters,
IndexUpgradeService indexUpgradeService,
IndexNameExpressionResolver indexNameExpressionResolver) {
super(settings, IndexUpgradeInfoAction.NAME, transportService, clusterService, threadPool, actionFilters,
indexNameExpressionResolver, Request::new);
this.indexUpgradeService = indexUpgradeService;
}
@Override
protected String executor() {
return ThreadPool.Names.GENERIC;
}
@Override
protected Response newResponse() {
return new Response();
}
@Override
protected ClusterBlockException checkBlock(Request request, ClusterState state) {
// Cluster is not affected but we look up repositories in metadata
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ);
}
@Override
protected final void masterOperation(final Request request, ClusterState state, final ActionListener<Response> listener) {
Map<String, UpgradeActionRequired> results =
indexUpgradeService.upgradeInfo(request.indices(), request.indicesOptions(), request.extraParams(), state);
listener.onResponse(new Response(results));
}
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.upgrade.rest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
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.upgrade.actions.IndexUpgradeInfoAction;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction.Request;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class RestIndexUpgradeInfoAction extends BaseRestHandler {
private final Set<String> extraParameters;
public RestIndexUpgradeInfoAction(Settings settings, RestController controller, Set<String> extraParameters) {
super(settings);
controller.registerHandler(RestRequest.Method.GET, "/_xpack/_upgrade", this);
controller.registerHandler(RestRequest.Method.GET, "{index}/_xpack/_upgrade", this);
this.extraParameters = extraParameters;
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
if (request.method().equals(RestRequest.Method.GET)) {
return handleGet(request, client);
} else {
throw new IllegalArgumentException("illegal method [" + request.method() + "] for request [" + request.path() + "]");
}
}
private RestChannelConsumer handleGet(final RestRequest request, NodeClient client) {
Request infoRequest = new Request(Strings.splitStringByCommaToArray(request.param("index")));
infoRequest.indicesOptions(IndicesOptions.fromRequest(request, infoRequest.indicesOptions()));
Map<String, String> extraParamsMap = new HashMap<>();
for (String param : extraParameters) {
String value = request.param(param);
if (value != null) {
extraParamsMap.put(param, value);
}
}
infoRequest.extraParams(extraParamsMap);
return channel -> client.execute(IndexUpgradeInfoAction.INSTANCE, infoRequest, new RestToXContentListener<>(channel));
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.upgrade;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import java.util.Collections;
import static org.hamcrest.core.IsEqual.equalTo;
public class IndexUpgradeCheckTests extends ESTestCase {
public void testGenericUpgradeCheck() {
IndexUpgradeCheck check = new GenericIndexUpgradeCheck();
assertThat(check.getName(), equalTo("generic"));
IndexMetaData goodIndex = newTestIndexMeta("good", Settings.EMPTY);
IndexMetaData badIndex = newTestIndexMeta("bad",
Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.fromString("2.0.0")).build());
assertThat(check.actionRequired(goodIndex, Collections.emptyMap(), ClusterState.EMPTY_STATE),
equalTo(UpgradeActionRequired.UP_TO_DATE));
assertThat(check.actionRequired(badIndex, Collections.emptyMap(), ClusterState.EMPTY_STATE),
equalTo(UpgradeActionRequired.REINDEX));
}
public void testInternalUpgradeCheck() {
IndexUpgradeCheck check = new InternalIndexUpgradeCheck();
assertThat(check.getName(), equalTo("inner"));
IndexMetaData goodKibanaIndex = newTestIndexMeta(".kibana", Settings.EMPTY);
assertThat(check.actionRequired(goodKibanaIndex, Collections.emptyMap(), ClusterState.EMPTY_STATE),
equalTo(UpgradeActionRequired.UPGRADE));
IndexMetaData renamedKibanaIndex = newTestIndexMeta(".kibana2", Settings.EMPTY);
assertThat(check.actionRequired(renamedKibanaIndex, Collections.emptyMap(), ClusterState.EMPTY_STATE),
equalTo(UpgradeActionRequired.NOT_APPLICABLE));
assertThat(check.actionRequired(renamedKibanaIndex, Collections.singletonMap("kibana_indices", ".kibana*"),
ClusterState.EMPTY_STATE), equalTo(UpgradeActionRequired.UPGRADE));
assertThat(check.actionRequired(renamedKibanaIndex, Collections.singletonMap("kibana_indices", ".kibana1,.kibana2"),
ClusterState.EMPTY_STATE), equalTo(UpgradeActionRequired.UPGRADE));
IndexMetaData watcherIndex = newTestIndexMeta(".watches", Settings.EMPTY);
assertThat(check.actionRequired(watcherIndex, Collections.singletonMap("kibana_indices", ".kibana*"), ClusterState.EMPTY_STATE),
equalTo(UpgradeActionRequired.UPGRADE));
IndexMetaData securityIndex = newTestIndexMeta(".security", Settings.EMPTY);
assertThat(check.actionRequired(securityIndex, Collections.singletonMap("kibana_indices", ".kibana*"), ClusterState.EMPTY_STATE),
equalTo(UpgradeActionRequired.UPGRADE));
}
public static IndexMetaData newTestIndexMeta(String name, Settings indexSettings) {
Settings build = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_CREATION_DATE, 1)
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
.put(IndexMetaData.SETTING_VERSION_UPGRADED, Version.V_5_0_0_beta1)
.put(indexSettings)
.build();
return IndexMetaData.builder(name).settings(build).build();
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.upgrade;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.ml.MachineLearning;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction.Response;
import java.util.Collection;
import java.util.Collections;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.core.IsEqual.equalTo;
public class IndexUpgradeIT extends ESIntegTestCase {
@Override
protected boolean ignoreExternalCluster() {
return true;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings.Builder settings = Settings.builder().put(super.nodeSettings(nodeOrdinal));
settings.put(MachineLearning.AUTODETECT_PROCESS.getKey(), false);
settings.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), false);
settings.put(XPackSettings.SECURITY_ENABLED.getKey(), false);
settings.put(XPackSettings.WATCHER_ENABLED.getKey(), false);
settings.put(XPackSettings.MONITORING_ENABLED.getKey(), false);
settings.put(XPackSettings.GRAPH_ENABLED.getKey(), false);
return settings.build();
}
@Override
protected Settings transportClientSettings() {
Settings.Builder settings = Settings.builder().put(super.transportClientSettings());
settings.put(MachineLearning.AUTODETECT_PROCESS.getKey(), false);
settings.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), false);
settings.put(XPackSettings.SECURITY_ENABLED.getKey(), false);
settings.put(XPackSettings.WATCHER_ENABLED.getKey(), false);
settings.put(XPackSettings.MONITORING_ENABLED.getKey(), false);
settings.put(XPackSettings.GRAPH_ENABLED.getKey(), false);
return settings.build();
}
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(XPackPlugin.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return nodePlugins();
}
public void testIndexUpgradeInfo() {
assertAcked(client().admin().indices().prepareCreate("test").get());
assertAcked(client().admin().indices().prepareCreate("kibana_test").get());
ensureYellow("test");
Response response = client().prepareExecute(IndexUpgradeInfoAction.INSTANCE).setIndices("test", "kibana_test")
.setExtraParams(Collections.singletonMap("kibana_indices", "kibana_test")).get();
logger.info("Got response [{}]", Strings.toString(response));
assertThat(response.getActions().size(), equalTo(1));
assertThat(response.getActions().get("kibana_test"), equalTo(UpgradeActionRequired.UPGRADE));
assertThat(Strings.toString(response), containsString("kibana_test"));
}
}

View File

@ -0,0 +1,168 @@
/*
* 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.upgrade;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.xpack.upgrade.IndexUpgradeCheckTests.newTestIndexMeta;
import static org.hamcrest.core.IsEqual.equalTo;
public class IndexUpgradeServiceTests extends ESTestCase {
private IndexUpgradeCheck upgradeBarCheck = new IndexUpgradeCheck() {
@Override
public String getName() {
return "upgrade_bar";
}
@Override
public UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state) {
if ("bar".equals(indexMetaData.getSettings().get("test.setting"))) {
return UpgradeActionRequired.UPGRADE;
}
return UpgradeActionRequired.NOT_APPLICABLE;
}
};
private IndexUpgradeCheck reindexFooCheck = new IndexUpgradeCheck() {
@Override
public String getName() {
return "reindex_foo";
}
@Override
public UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state) {
if ("foo".equals(indexMetaData.getSettings().get("test.setting"))) {
return UpgradeActionRequired.REINDEX;
}
return UpgradeActionRequired.NOT_APPLICABLE;
}
};
private IndexUpgradeCheck everythingIsFineCheck = new IndexUpgradeCheck() {
@Override
public String getName() {
return "everything_is_fine";
}
@Override
public UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state) {
return UpgradeActionRequired.UP_TO_DATE;
}
};
private IndexUpgradeCheck unreachableCheck = new IndexUpgradeCheck() {
@Override
public String getName() {
return "unreachable";
}
@Override
public UpgradeActionRequired actionRequired(IndexMetaData indexMetaData, Map<String, String> params, ClusterState state) {
fail("Unreachable check is called");
return null;
}
};
public void testIndexUpgradeServiceMultipleCheck() {
IndexUpgradeService service;
if (randomBoolean()) {
service = new IndexUpgradeService(Settings.EMPTY, Arrays.asList(
upgradeBarCheck,
reindexFooCheck,
everythingIsFineCheck,
unreachableCheck // This one should never be called
));
} else {
service = new IndexUpgradeService(Settings.EMPTY, Arrays.asList(
reindexFooCheck,
upgradeBarCheck,
everythingIsFineCheck,
unreachableCheck // This one should never be called
));
}
IndexMetaData fooIndex = newTestIndexMeta("bar", Settings.builder().put("test.setting", "bar").build());
IndexMetaData barIndex = newTestIndexMeta("foo", Settings.builder().put("test.setting", "foo").build());
IndexMetaData bazIndex = newTestIndexMeta("baz", Settings.EMPTY);
ClusterState clusterState = mockClusterState(fooIndex, barIndex, bazIndex);
Map<String, UpgradeActionRequired> result = service.upgradeInfo(new String[]{"bar", "foo", "baz"},
IndicesOptions.lenientExpandOpen(), Collections.emptyMap(), clusterState);
assertThat(result.size(), equalTo(2));
assertThat(result.get("bar"), equalTo(UpgradeActionRequired.UPGRADE));
assertThat(result.get("foo"), equalTo(UpgradeActionRequired.REINDEX));
result = service.upgradeInfo(new String[]{"b*"}, IndicesOptions.lenientExpandOpen(), Collections.emptyMap(), clusterState);
assertThat(result.size(), equalTo(1));
assertThat(result.get("bar"), equalTo(UpgradeActionRequired.UPGRADE));
}
public void testNoMatchingChecks() {
IndexUpgradeService service = new IndexUpgradeService(Settings.EMPTY, Arrays.asList(
upgradeBarCheck,
reindexFooCheck
));
IndexMetaData fooIndex = newTestIndexMeta("bar", Settings.builder().put("test.setting", "bar").build());
IndexMetaData barIndex = newTestIndexMeta("foo", Settings.builder().put("test.setting", "foo").build());
IndexMetaData bazIndex = newTestIndexMeta("baz", Settings.EMPTY);
ClusterState clusterState = mockClusterState(fooIndex, barIndex, bazIndex);
Map<String, UpgradeActionRequired> result = service.upgradeInfo(new String[]{"bar", "foo", "baz"},
IndicesOptions.lenientExpandOpen(), Collections.emptyMap(), clusterState);
assertThat(result.size(), equalTo(2));
assertThat(result.get("bar"), equalTo(UpgradeActionRequired.UPGRADE));
assertThat(result.get("foo"), equalTo(UpgradeActionRequired.REINDEX));
}
public void testEarlierChecksWin() {
IndexUpgradeService service = new IndexUpgradeService(Settings.EMPTY, Arrays.asList(
everythingIsFineCheck,
upgradeBarCheck,
reindexFooCheck
));
IndexMetaData fooIndex = newTestIndexMeta("bar", Settings.builder().put("test.setting", "bar").build());
IndexMetaData barIndex = newTestIndexMeta("foo", Settings.builder().put("test.setting", "foo").build());
IndexMetaData bazIndex = newTestIndexMeta("baz", Settings.EMPTY);
ClusterState clusterState = mockClusterState(fooIndex, barIndex, bazIndex);
Map<String, UpgradeActionRequired> result = service.upgradeInfo(new String[]{"bar", "foo", "baz"},
IndicesOptions.lenientExpandOpen(), Collections.emptyMap(), clusterState);
assertThat(result.size(), equalTo(0)); // everything as the first checker should indicate that everything is fine
}
private ClusterState mockClusterState(IndexMetaData... indices) {
MetaData.Builder metaDataBuilder = MetaData.builder();
for (IndexMetaData indexMetaData : indices) {
metaDataBuilder.put(indexMetaData, false);
}
return ClusterState.builder(ClusterName.DEFAULT).metaData(metaDataBuilder).build();
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.upgrade.actions;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.test.AbstractStreamableTestCase;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction.Request;
import java.util.Collections;
public class IndexUpgradeInfoActionRequestTests extends AbstractStreamableTestCase<Request> {
@Override
protected Request createTestInstance() {
int indexCount = randomInt(4);
String[] indices = new String[indexCount];
for (int i = 0; i < indexCount; i++) {
indices[i] = randomAlphaOfLength(10);
}
Request request = new Request(indices);
if (randomBoolean()) {
request.extraParams(Collections.singletonMap(randomAlphaOfLength(10), randomAlphaOfLength(20)));
}
if (randomBoolean()) {
request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()));
}
return request;
}
@Override
protected Request createBlankInstance() {
return new Request();
}
}

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.upgrade.actions;
import org.elasticsearch.test.AbstractStreamableTestCase;
import org.elasticsearch.xpack.upgrade.UpgradeActionRequired;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction.Response;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
public class IndexUpgradeInfoActionResponseTests extends AbstractStreamableTestCase<Response> {
@Override
protected Response createTestInstance() {
int actionsCount = randomIntBetween(0, 5);
Map<String, UpgradeActionRequired> actions = new HashMap<>(actionsCount);
for (int i = 0; i < actionsCount; i++) {
actions.put(randomAlphaOfLength(10), randomFrom(EnumSet.allOf(UpgradeActionRequired.class)));
}
return new Response(actions);
}
@Override
protected Response createBlankInstance() {
return new Response();
}
}

View File

@ -156,3 +156,4 @@ indices:data/write/update/byquery
indices:data/write/delete/byquery indices:data/write/delete/byquery
indices:data/write/reindex indices:data/write/reindex
cluster:admin/xpack/deprecation/info cluster:admin/xpack/deprecation/info
cluster:admin/xpack/upgrade/info

View File

@ -0,0 +1,42 @@
{
"xpack.upgrade.info": {
"methods": [ "GET" ],
"url": {
"path": "/_xpack/_upgrade",
"paths": [
"/_xpack/_upgrade",
"/{index}/_xpack/_upgrade"
],
"parts": {
"index": {
"type" : "list",
"description" : "A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices"
}
},
"params": {
"allow_no_indices": {
"type" : "boolean",
"description" : "Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)"
},
"expand_wildcards": {
"type" : "enum",
"options" : ["open","closed","none","all"],
"default" : "open",
"description" : "Whether to expand wildcard expression to concrete indices that are open, closed or both."
},
"ignore_unavailable": {
"type" : "boolean",
"description" : "Whether specified concrete indices should be ignored when unavailable (missing or closed)"
},
"wait_for_completion": {
"type" : "boolean",
"description" : "Specify whether the request should block until the all segments are upgraded (default: false)"
},
"kibana_indices": {
"type": "list",
"description": "A comma separated list of indices that should be treated as kibana indices"
}
}
}
}
}

View File

@ -0,0 +1,24 @@
---
setup:
- do:
indices.create:
index: test1
- do:
indices.create:
index: test2
---
"Index - all":
- do:
xpack.upgrade.info: { index: _all }
- length: { indices: 0 }
---
"Index - treat test2 as kibana":
- do:
xpack.upgrade.info: { index: _all, kibana_indices: test2 }
- length: { indices: 1 }
- match: { indices.test2.action_required: "upgrade" }