diff --git a/x-pack/plugin/build.gradle b/x-pack/plugin/build.gradle index 90786f7c0c678..d8073cc28f5a0 100644 --- a/x-pack/plugin/build.gradle +++ b/x-pack/plugin/build.gradle @@ -143,6 +143,7 @@ tasks.named("yamlRestCompatTestTransform").configure({ task -> task.skipTest("ml/sparse_vector_search/Search on a sparse_vector field with dots in the field names", "Vectors are no longer returned by default") task.skipTest("ml/sparse_vector_search/Search on a nested sparse_vector field with dots in the field names and conflicting child fields", "Vectors are no longer returned by default") task.skipTest("esql/190_lookup_join/lookup-no-key-only-key", "Requires the fix") + task.skipTest("ml/data_frame_analytics_crud/Test put config with remote source index", "Error message changed") }) tasks.named('yamlRestCompatTest').configure { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java index 700158712707a..93a2d798c3bbb 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java @@ -69,7 +69,8 @@ public final class SourceDestValidator { + "alias [{0}], at least a [{1}] license is required, found license [{2}]"; public static final String REMOTE_CLUSTER_LICENSE_INACTIVE = "License check failed for remote cluster " + "alias [{0}], license is not active"; - public static final String REMOTE_SOURCE_INDICES_NOT_SUPPORTED = "remote source indices are not supported"; + public static final String REMOTE_SOURCE_AND_CROSS_PROJECT_INDICES_ARE_NOT_SUPPORTED = + "remote source and cross-project indices are not supported"; public static final String REMOTE_CLUSTERS_TRANSPORT_TOO_OLD = "remote clusters are expected to run at least version [{0}] (reason: [{1}])," + " but the following clusters were too old: [{2}]"; public static final String PIPELINE_MISSING = "Pipeline with id [{0}] could not be found"; @@ -555,7 +556,7 @@ static class RemoteSourceNotSupportedValidation implements SourceDestValidation @Override public void validate(Context context, ActionListener listener) { if (context.resolveRemoteSource().isEmpty() == false) { - context.addValidationError(REMOTE_SOURCE_INDICES_NOT_SUPPORTED); + context.addValidationError(REMOTE_SOURCE_AND_CROSS_PROJECT_INDICES_ARE_NOT_SUPPORTED); } listener.onResponse(context); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidatorTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidatorTests.java index 8ba3fe338caa1..81830dad58ef7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidatorTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidatorTests.java @@ -67,7 +67,9 @@ import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.DESTINATION_IN_SOURCE_VALIDATION; import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.DESTINATION_PIPELINE_MISSING_VALIDATION; import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.DESTINATION_SINGLE_INDEX_VALIDATION; +import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.REMOTE_SOURCE_NOT_SUPPORTED_VALIDATION; import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.SOURCE_MISSING_VALIDATION; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -667,6 +669,51 @@ public void testRemoteSourceBasic() throws InterruptedException { ); } + public void testRemoteSourceNotSupportedValidationWithLocalIndex() throws InterruptedException { + Context context = spy( + new SourceDestValidator.Context( + CLUSTER_STATE, + indexNameExpressionResolver, + remoteClusterService, + remoteClusterLicenseCheckerBasic, + ingestService, + new String[] { SOURCE_1 }, + "dest", + null, + "node_id", + "license" + ) + ); + + assertValidationWithContext(listener -> REMOTE_SOURCE_NOT_SUPPORTED_VALIDATION.validate(context, listener), c -> { + assertNull(c.getValidationException()); + }, null); + } + + public void testRemoteSourceNotSupportedValidationWithRemoteIndex() throws InterruptedException { + Context context = spy( + new SourceDestValidator.Context( + CLUSTER_STATE, + indexNameExpressionResolver, + remoteClusterService, + remoteClusterLicenseCheckerBasic, + ingestService, + new String[] { REMOTE_BASIC + ":" + SOURCE_1 }, + "dest", + null, + "node_id", + "license" + ) + ); + + assertValidationWithContext(listener -> REMOTE_SOURCE_NOT_SUPPORTED_VALIDATION.validate(context, listener), c -> { + assertThat( + c.getValidationException().getMessage(), + containsString("remote source and cross-project indices are not supported") + ); + }, null); + } + public void testRemoteSourcePlatinum() throws InterruptedException { final Context context = spy( new SourceDestValidator.Context( diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/DataframeCpsIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/DataframeCpsIT.java new file mode 100644 index 0000000000000..bff35219b9a5d --- /dev/null +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/DataframeCpsIT.java @@ -0,0 +1,85 @@ +/* + * 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.ml.integration; + +import org.elasticsearch.common.ValidationException; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.plugins.ClusterPlugin; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.xpack.core.ml.action.PutDataFrameAnalyticsAction; +import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig; +import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsDest; +import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsSource; +import org.elasticsearch.xpack.core.ml.dataframe.analyses.BoostedTreeParams; +import org.elasticsearch.xpack.core.ml.dataframe.analyses.Classification; +import org.elasticsearch.xpack.core.ml.utils.QueryProvider; +import org.elasticsearch.xpack.ml.MlSingleNodeTestCase; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import static org.hamcrest.Matchers.containsString; + +public class DataframeCpsIT extends MlSingleNodeTestCase { + @Override + protected Settings nodeSettings() { + return Settings.builder().put(super.nodeSettings()).put("serverless.cross_project.enabled", "true").build(); + } + + @Override + protected Collection> getPlugins() { + return Stream.concat(super.getPlugins().stream(), Stream.of(CpsPlugin.class)).toList(); + } + + public void testCrossProjectFailsForDataFrameAnalytics() throws IOException { + var id = "test-cross-project-fails"; + var sourceIndex = "project1:" + id + "_source_index"; + var destIndex = id + "_results"; + + var config = new DataFrameAnalyticsConfig.Builder().setId(id) + .setSource( + new DataFrameAnalyticsSource( + new String[] { sourceIndex }, + QueryProvider.fromParsedQuery(QueryBuilders.matchAllQuery()), + null, + Collections.emptyMap() + ) + ) + .setDest(new DataFrameAnalyticsDest(destIndex, null)) + .setAnalysis( + new Classification( + "keyword-field", + BoostedTreeParams.builder().setNumTopFeatureImportanceValues(1).build(), + null, + null, + null, + null, + null, + null, + null + ) + ) + .build(); + + var request = new PutDataFrameAnalyticsAction.Request(config); + var response = client().execute(PutDataFrameAnalyticsAction.INSTANCE, request); + var validationException = assertThrows(ValidationException.class, response::actionGet); + assertThat(validationException.getMessage(), containsString("remote source and cross-project indices are not supported")); + } + + public static class CpsPlugin extends Plugin implements ClusterPlugin { + public List> getSettings() { + return List.of(Setting.simpleString("serverless.cross_project.enabled", Setting.Property.NodeScope)); + } + } +} diff --git a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityMlIT.java b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityMlIT.java index 5b5d4617b5c44..5210cfe7f7d8e 100644 --- a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityMlIT.java +++ b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityMlIT.java @@ -200,7 +200,7 @@ public void testDataframeAnalyticsNotSupportForRemoteIndices() { """); final ResponseException e = expectThrows(ResponseException.class, () -> performRequestWithRemoteMlUser(putDataframeAnalytics)); assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(400)); - assertThat(e.getMessage(), containsString("remote source indices are not supported")); + assertThat(e.getMessage(), containsString("remote source and cross-project indices are not supported")); } private Response performRequestWithRemoteMlUser(final Request request) throws IOException { diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/data_frame_analytics_crud.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/data_frame_analytics_crud.yml index e3f1a274c0a5b..0ff6805b8806f 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/data_frame_analytics_crud.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/data_frame_analytics_crud.yml @@ -432,7 +432,7 @@ setup: "Test put config with remote source index": - do: - catch: /.*Validation Failed.* remote source indices are not supported/ + catch: /.*Validation Failed.* remote source and cross-project indices are not supported/ ml.put_data_frame_analytics: id: "config-with-missing-concrete-source-index" body: >