Skip to content

Commit

Permalink
Add API for resetting state of a SystemIndexPlugin (#69469)
Browse files Browse the repository at this point in the history
When we disable access to system indices, plugins will still need
a way to erase their state. The obvious and most pressing use
case for this is in tests, which need to be able to clean up the
state of a cluster in between groups of tests.

* Use a HandledTransportAction for reset action

My initial cut used a TransportMasterNodeAction, which requires code
that carefully manipulates cluster state. At least for the first cut and
testing, it seems like it will be much easier to use a client within a
HandledTransportAction, which effectively makes the
TransportResetFeatureStateAction a class that dispatches other transport
actions to do the real work.

* Clean up code by using a GroupedActionListener

* ML feature state cleaner

* Implement Transform feature state reset

* Change _features/reset path to _features/_reset

Out of an abundance of caution, I think the "reset" part of this path
should have a leading underscore, so that if there's ever a reason to
implement "GET _features/<feature_id>" we won't have to worry about
distinguishing "reset" from a feature name.

Co-authored-by: Gordon Brown <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
3 people authored Mar 17, 2021
1 parent 7f31977 commit 624ee45
Show file tree
Hide file tree
Showing 31 changed files with 983 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
package org.elasticsearch.client;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.snapshots.GetFeaturesRequest;
import org.elasticsearch.client.snapshots.GetFeaturesResponse;
import org.elasticsearch.client.feature.GetFeaturesRequest;
import org.elasticsearch.client.feature.GetFeaturesResponse;
import org.elasticsearch.client.feature.ResetFeaturesRequest;
import org.elasticsearch.client.feature.ResetFeaturesResponse;

import java.io.IOException;

Expand Down Expand Up @@ -71,4 +73,50 @@ public Cancellable getFeaturesAsync(
emptySet()
);
}

/**
* Reset the state of Elasticsearch features, deleting system indices and performing other
* cleanup operations.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/reset-features-api.html"> Rest
* Features API on elastic.co</a>
*
* @param resetFeaturesRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public ResetFeaturesResponse resetFeatures(ResetFeaturesRequest resetFeaturesRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(
resetFeaturesRequest,
FeaturesRequestConverters::resetFeatures,
options,
ResetFeaturesResponse::parse,
emptySet()
);
}

/**
* Asynchronously reset the state of Elasticsearch features, deleting system indices and performing other
* cleanup operations.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-features-api.html"> Get Snapshottable
* Features API on elastic.co</a>
*
* @param resetFeaturesRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
* @return cancellable that may be used to cancel the request
*/
public Cancellable resetFeaturesAsync(
ResetFeaturesRequest resetFeaturesRequest, RequestOptions options,
ActionListener<ResetFeaturesResponse> listener) {
return restHighLevelClient.performRequestAsyncAndParseEntity(
resetFeaturesRequest,
FeaturesRequestConverters::resetFeatures,
options,
ResetFeaturesResponse::parse,
listener,
emptySet()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
package org.elasticsearch.client;

import org.apache.http.client.methods.HttpGet;
import org.elasticsearch.client.snapshots.GetFeaturesRequest;
import org.apache.http.client.methods.HttpPost;
import org.elasticsearch.client.feature.GetFeaturesRequest;
import org.elasticsearch.client.feature.ResetFeaturesRequest;

public class FeaturesRequestConverters {

Expand All @@ -23,4 +25,9 @@ static Request getFeatures(GetFeaturesRequest getFeaturesRequest) {
request.addParameters(parameters.asMap());
return request;
}

static Request resetFeatures(ResetFeaturesRequest resetFeaturesRequest) {
String endpoint = "/_features/_reset";
return new Request(HttpPost.METHOD_NAME, endpoint);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

package org.elasticsearch.client.snapshots;
package org.elasticsearch.client.feature;

import org.elasticsearch.client.TimedRequest;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

package org.elasticsearch.client.snapshots;
package org.elasticsearch.client.feature;

import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.client.feature;

import org.elasticsearch.client.TimedRequest;

public class ResetFeaturesRequest extends TimedRequest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.client.feature;

import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;

import java.util.List;

public class ResetFeaturesResponse {
private final List<ResetFeatureStateStatus> features;

private static final ParseField FEATURES = new ParseField("features");

@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<ResetFeaturesResponse, Void> PARSER = new ConstructingObjectParser<>(
"snapshottable_features_response", true,
(a, ctx) -> new ResetFeaturesResponse((List<ResetFeatureStateStatus>) a[0])
);

static {
PARSER.declareObjectArray(
ConstructingObjectParser.constructorArg(),
ResetFeaturesResponse.ResetFeatureStateStatus::parse, FEATURES);
}

public ResetFeaturesResponse(List<ResetFeatureStateStatus> features) {
this.features = features;
}

public List<ResetFeatureStateStatus> getFeatures() {
return features;
}

public static ResetFeaturesResponse parse(XContentParser parser) {
return PARSER.apply(parser, null);
}

public static class ResetFeatureStateStatus {
private final String featureName;
private final String status;

private static final ParseField FEATURE_NAME = new ParseField("feature_name");
private static final ParseField STATUS = new ParseField("status");

private static final ConstructingObjectParser<ResetFeatureStateStatus, Void> PARSER = new ConstructingObjectParser<>(
"features", true, (a, ctx) -> new ResetFeatureStateStatus((String) a[0], (String) a[1])
);

static {
PARSER.declareField(ConstructingObjectParser.constructorArg(),
(p, c) -> p.text(), FEATURE_NAME, ObjectParser.ValueType.STRING);
PARSER.declareField(ConstructingObjectParser.constructorArg(),
(p, c) -> p.text(), STATUS, ObjectParser.ValueType.STRING);
}

ResetFeatureStateStatus(String featureName, String status) {
this.featureName = featureName;
this.status = status;
}

public static ResetFeatureStateStatus parse(XContentParser parser, Void ctx) {
return PARSER.apply(parser, ctx);
}

public String getFeatureName() {
return featureName;
}

public String getStatus() {
return status;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

package org.elasticsearch.client;

import org.elasticsearch.client.snapshots.GetFeaturesRequest;
import org.elasticsearch.client.snapshots.GetFeaturesResponse;
import org.elasticsearch.client.feature.GetFeaturesRequest;
import org.elasticsearch.client.feature.GetFeaturesResponse;
import org.elasticsearch.client.feature.ResetFeaturesRequest;
import org.elasticsearch.client.feature.ResetFeaturesResponse;

import java.io.IOException;

Expand All @@ -28,4 +30,17 @@ public void testGetFeatures() throws IOException {
assertThat(response.getFeatures().size(), greaterThan(1));
assertTrue(response.getFeatures().stream().anyMatch(feature -> "tasks".equals(feature.getFeatureName())));
}

public void testResetFeatures() throws IOException {
ResetFeaturesRequest request = new ResetFeaturesRequest();

ResetFeaturesResponse response = execute(request,
highLevelClient().features()::resetFeatures, highLevelClient().features()::resetFeaturesAsync);

assertThat(response, notNullValue());
assertThat(response.getFeatures(), notNullValue());
assertThat(response.getFeatures().size(), greaterThan(1));
assertTrue(response.getFeatures().stream().anyMatch(
feature -> "tasks".equals(feature.getFeatureName()) && "SUCCESS".equals(feature.getStatus())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.elasticsearch.client.snapshots;

import org.elasticsearch.client.AbstractResponseTestCase;
import org.elasticsearch.client.feature.GetFeaturesResponse;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"features.reset_features":{
"documentation":{
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-snapshots.html",
"description":"Resets the internal state of features, usually by deleting system indices"
},
"stability":"experimental",
"visibility":"public",
"headers":{
"accept": [ "application/json"]
},
"url":{
"paths":[
{
"path":"/_features/_reset",
"methods":[
"POST"
]
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"Get Features":
- skip:
features: contains
version: " - 7.99.99" # Adjust this after backport
reason: "This API was added in 7.13.0"
- do: { features.get_features: {}}
- contains: {'features': {'name': 'tasks'}}
Loading

0 comments on commit 624ee45

Please sign in to comment.