Skip to content

Commit

Permalink
[Transform] Surface script deprecation warnings in deprecation info A…
Browse files Browse the repository at this point in the history
…PI (#84040)
  • Loading branch information
przemekwitek committed Feb 22, 2022
1 parent 6cfae7f commit f69485b
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@
public final class OriginSettingClient extends FilterClient {

private final String origin;
private final boolean preserveResponseHeaders;

public OriginSettingClient(Client in, String origin) {
this(in, origin, false);
}

public OriginSettingClient(Client in, String origin, boolean preserveResponseHeaders) {
super(in);
this.origin = origin;
this.preserveResponseHeaders = preserveResponseHeaders;
}

@Override
Expand All @@ -38,7 +44,9 @@ protected <Request extends ActionRequest, Response extends ActionResponse> void
Request request,
ActionListener<Response> listener
) {
final Supplier<ThreadContext.StoredContext> supplier = in().threadPool().getThreadContext().newRestorableContext(false);
final Supplier<ThreadContext.StoredContext> supplier = in().threadPool()
.getThreadContext()
.newRestorableContext(preserveResponseHeaders);
try (ThreadContext.StoredContext ignore = in().threadPool().getThreadContext().stashWithOrigin(origin)) {
super.doExecute(action, request, new ContextPreservingActionListener<>(supplier, listener));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class TransformDeprecations {

public static final String AGGS_BREAKING_CHANGES_URL = "https://ela.st/es-deprecation-8-transform-aggregation-options";

public static final String PAINLESS_BREAKING_CHANGES_URL = "https://ela.st/es-deprecation-8-transform-painless-options";

public static final String ACTION_UPGRADE_TRANSFORMS_API =
"This transform configuration uses obsolete syntax which will be unsupported after the next upgrade. "
+ "Use [/_transform/_upgrade] to upgrade all transforms to the latest format.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,37 @@

package org.elasticsearch.xpack.deprecation;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.common.logging.HeaderWarning;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.CountDown;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.xpack.core.ClientHelper;
import org.elasticsearch.xpack.core.action.util.PageParams;
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
import org.elasticsearch.xpack.core.transform.TransformDeprecations;
import org.elasticsearch.xpack.core.transform.TransformField;
import org.elasticsearch.xpack.core.transform.action.GetTransformAction;
import org.elasticsearch.xpack.core.transform.action.ValidateTransformAction;
import org.elasticsearch.xpack.core.transform.transforms.TransformConfig;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

import static java.util.stream.Collectors.toList;

public class TransformDeprecationChecker implements DeprecationChecker {

public static final String TRANSFORM_DEPRECATION_KEY = "transform_settings";

private static final Logger logger = LogManager.getLogger(TransformDeprecationChecker.class);

@Override
public boolean enabled(Settings settings) {
// always enabled
Expand All @@ -32,7 +48,7 @@ public boolean enabled(Settings settings) {
public void check(Components components, ActionListener<CheckResult> deprecationIssueListener) {

PageParams startPage = new PageParams(0, PageParams.DEFAULT_SIZE);
List<DeprecationIssue> issues = new ArrayList<>();
Collection<DeprecationIssue> issues = new ConcurrentLinkedQueue<>();
recursiveGetTransformsAndCollectDeprecations(
components,
issues,
Expand All @@ -51,25 +67,82 @@ public String getName() {

private void recursiveGetTransformsAndCollectDeprecations(
Components components,
List<DeprecationIssue> issues,
Collection<DeprecationIssue> issues,
PageParams page,
ActionListener<List<DeprecationIssue>> listener
) {
final GetTransformAction.Request request = new GetTransformAction.Request(Metadata.ALL);
request.setPageParams(page);
request.setAllowNoResources(true);

components.client().execute(GetTransformAction.INSTANCE, request, ActionListener.wrap(getTransformResponse -> {
for (TransformConfig config : getTransformResponse.getTransformConfigurations()) {
issues.addAll(config.checkForDeprecations(components.xContentRegistry()));
}
if (getTransformResponse.getCount() >= (page.getFrom() + page.getSize())) {
PageParams nextPage = new PageParams(page.getFrom() + page.getSize(), PageParams.DEFAULT_SIZE);
recursiveGetTransformsAndCollectDeprecations(components, issues, nextPage, listener);
} else {
listener.onResponse(issues);
}

}, listener::onFailure));
ClientHelper.executeAsyncWithOrigin(
components.client(),
ClientHelper.DEPRECATION_ORIGIN,
GetTransformAction.INSTANCE,
request,
ActionListener.wrap(getTransformResponse -> {
final int numberOfTransforms = getTransformResponse.getTransformConfigurations().size();
Runnable processNextPage = () -> {
if (getTransformResponse.getCount() >= (page.getFrom() + page.getSize())) {
PageParams nextPage = new PageParams(page.getFrom() + page.getSize(), PageParams.DEFAULT_SIZE);
recursiveGetTransformsAndCollectDeprecations(components, issues, nextPage, listener);
} else {
listener.onResponse(new ArrayList<>(issues));
}
};
if (numberOfTransforms == 0) {
processNextPage.run();
return;
}
final CountDown numberOfResponsesToProcess = new CountDown(numberOfTransforms);
for (TransformConfig config : getTransformResponse.getTransformConfigurations()) {
issues.addAll(config.checkForDeprecations(components.xContentRegistry()));

ValidateTransformAction.Request validateTransformRequest = new ValidateTransformAction.Request(
config,
false,
TimeValue.timeValueSeconds(30)
);
ActionListener<ValidateTransformAction.Response> validateTransformListener = ActionListener.wrap(
validateTransformResponse -> {
List<String> warningHeaders = components.client()
.threadPool()
.getThreadContext()
.getResponseHeaders()
.get("Warning");
if (warningHeaders != null) {
issues.addAll(
warningHeaders.stream()
.map(warningHeader -> createDeprecationIssue(config.getId(), warningHeader))
.collect(toList())
);
}
if (numberOfResponsesToProcess.countDown()) {
processNextPage.run();
}
},
e -> {
logger.warn("An exception occurred while gathering deprecation warnings for transform", e);
if (numberOfResponsesToProcess.countDown()) {
processNextPage.run();
}
}
);

components.client().execute(ValidateTransformAction.INSTANCE, validateTransformRequest, validateTransformListener);
}
}, listener::onFailure)
);
}

private static DeprecationIssue createDeprecationIssue(String transformId, String warningHeader) {
return new DeprecationIssue(
DeprecationIssue.Level.WARNING,
"Transform [" + transformId + "]: " + HeaderWarning.extractWarningValueFromWarningHeader(warningHeader, true),
TransformDeprecations.PAINLESS_BREAKING_CHANGES_URL,
null,
false,
Collections.singletonMap(TransformField.TRANSFORM_ID, transformId)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ protected final void masterOperation(
DeprecationChecker.Components components = new DeprecationChecker.Components(
xContentRegistry,
settings,
new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN),
new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN, true),
state
);
pluginSettingIssues(PLUGIN_CHECKERS, components, ActionListener.wrap(deprecationIssues -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.deprecation;

import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;

import static org.hamcrest.Matchers.is;

public class TransformDeprecationCheckerTests extends ESTestCase {

public void testEnabled() {
TransformDeprecationChecker transformDeprecationChecker = new TransformDeprecationChecker();
assertThat(transformDeprecationChecker.enabled(Settings.EMPTY), is(true));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.transform.integration;

import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.WarningsHandler;
import org.junit.Before;

import java.io.IOException;
import java.util.Arrays;

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;

public class TransformDeprecationIT extends TransformRestTestCase {

private static final String TEST_USER_NAME = "transform_admin_plus_data";
private static final String DATA_ACCESS_ROLE = "test_data_access";

private static boolean indicesCreated = false;

// preserve indices in order to reuse source indices in several test cases
@Override
protected boolean preserveIndicesUponCompletion() {
return true;
}

@Before
public void createIndexes() throws IOException {
setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME);
setupUser(TEST_USER_NAME, Arrays.asList("transform_admin", DATA_ACCESS_ROLE));

// it's not possible to run it as @BeforeClass as clients aren't initialized then, so we need this little hack
if (indicesCreated) {
return;
}
createReviewsIndex();
indicesCreated = true;
}

public void testDeprecationInfo() throws Exception {
{
Request deprecationInfoRequest = new Request("GET", "_migration/deprecations");
Response deprecationInfoResponse = client().performRequest(deprecationInfoRequest);
assertThat(EntityUtils.toString(deprecationInfoResponse.getEntity()), not(containsString("Use of the joda time method")));
}
{
String transformId = "script_deprecated_syntax";
Request createTransformRequest = new Request("PUT", getTransformEndpoint() + transformId);
// We need this as creation of transform with deprecated script syntax triggers warnings
createTransformRequest.setOptions(RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE));
String config = createTransformConfig(REVIEWS_INDEX_NAME, transformId);
createTransformRequest.setJsonEntity(config);
client().performRequest(createTransformRequest);
}
{
Request deprecationInfoRequest = new Request("GET", "_migration/deprecations");
deprecationInfoRequest.setOptions(RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE));
Response deprecationInfoResponse = client().performRequest(deprecationInfoRequest);
assertThat(
EntityUtils.toString(deprecationInfoResponse.getEntity()),
allOf(
containsString("Use of the joda time method [getMillis()] is deprecated. Use [toInstant().toEpochMilli()] instead."),
containsString("Use of the joda time method [getEra()] is deprecated. Use [get(ChronoField.ERA)] instead.")
)
);
}
}

private static String createTransformConfig(String sourceIndex, String destinationIndex) {
return "{"
+ " \"source\": {"
+ " \"index\": \""
+ sourceIndex
+ "\","
+ " \"runtime_mappings\": {"
+ " \"timestamp-5m\": {"
+ " \"type\": \"date\","
+ " \"script\": {"
// We don't use "era" for anything in this script. This is solely to generate the deprecation warning.
+ " \"source\": \"def era = doc['timestamp'].value.era; emit(doc['timestamp'].value.millis)\""
+ " }"
+ " }"
+ " }"
+ " },"
+ " \"dest\": {"
+ " \"index\": \""
+ destinationIndex
+ "\""
+ " },"
+ " \"pivot\": {"
+ " \"group_by\": {"
+ " \"timestamp\": {"
+ " \"date_histogram\": {"
+ " \"field\": \"timestamp-5m\","
+ " \"calendar_interval\": \"1m\""
+ " }"
+ " }"
+ " },"
+ " \"aggregations\": {"
+ " \"bytes.avg\": {"
+ " \"avg\": {"
+ " \"field\": \"bytes\""
+ " }"
+ " },"
+ " \"millis\": {"
+ " \"scripted_metric\": {"
+ " \"init_script\": \"state.m = 0\","
+ " \"map_script\": \"state.m = doc['timestamp'].value.millis;\","
+ " \"combine_script\": \"return state.m;\","
+ " \"reduce_script\": \"def last = 0; for (s in states) {last = s;} return last;\""
+ " }"
+ " }"
+ " }"
+ " }"
+ "}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,11 @@ private SearchRequest buildSearchRequest(SourceConfig sourceConfig, Map<String,
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(sourceConfig.getQueryConfig().getQuery())
.runtimeMappings(sourceConfig.getRuntimeMappings());
buildSearchQuery(sourceBuilder, null, pageSize);
return new SearchRequest(sourceConfig.getIndex()).source(sourceBuilder).indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
return new SearchRequest(sourceConfig.getIndex()).indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN)
.source(sourceBuilder)
// This is done so that 2 consecutive queries return the same warnings in response headers.
// If the request is cached, the second time it is called the response headers are not preserved.
.requestCache(false);
}

@Override
Expand Down

0 comments on commit f69485b

Please sign in to comment.