Skip to content

Commit 49fee4a

Browse files
committed
Allow using test inference service on the local cluster in newToOld ccs tests.
1 parent 59f1723 commit 49fee4a

File tree

8 files changed

+81
-15
lines changed

8 files changed

+81
-15
lines changed

x-pack/plugin/esql/qa/server/multi-clusters/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ dependencies {
2121
javaRestTestImplementation project(xpackModule('esql:qa:testFixtures'))
2222
javaRestTestImplementation project(xpackModule('esql:qa:server'))
2323
javaRestTestImplementation project(xpackModule('esql'))
24+
25+
clusterPlugins project(':x-pack:plugin:inference:qa:test-service-plugin')
2426
}
2527

2628
def supportedVersion = bwcVersion -> {

x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/Clusters.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public static ElasticsearchCluster localCluster(ElasticsearchCluster remoteClust
5454
if (supportRetryOnShardFailures(version) == false) {
5555
cluster.setting("cluster.routing.rebalance.enable", "none");
5656
}
57+
if (localClusterSupportsInferenceTestService()) {
58+
cluster.plugin("inference-service-test");
59+
}
5760
return cluster.build();
5861
}
5962

@@ -73,6 +76,22 @@ public static org.elasticsearch.Version bwcVersion() {
7376
return local.before(remote) ? local : remote;
7477
}
7578

79+
public static boolean localClusterSupportsInferenceTestService() {
80+
return isNewToOld() && localClusterVersion().onOrAfter(org.elasticsearch.Version.fromString("9.3.0"));
81+
}
82+
83+
/**
84+
* Returns true if the current task is a "newToOld" BWC test.
85+
* Checks the tests.task system property to determine the task type.
86+
*/
87+
public static boolean isNewToOld() {
88+
String taskName = System.getProperty("tests.task");
89+
if (taskName == null) {
90+
return false;
91+
}
92+
return taskName.endsWith("#newToOld");
93+
}
94+
7695
private static Version distributionVersion(String key) {
7796
final String val = System.getProperty(key);
7897
return val != null ? Version.fromString(val) : Version.CURRENT;

x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,17 @@
4848
import static org.elasticsearch.xpack.esql.CsvTestsDataLoader.CSV_DATASET_MAP;
4949
import static org.elasticsearch.xpack.esql.CsvTestsDataLoader.ENRICH_SOURCE_INDICES;
5050
import static org.elasticsearch.xpack.esql.EsqlTestUtils.classpathResources;
51+
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.COMPLETION;
5152
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.ENABLE_LOOKUP_JOIN_ON_REMOTE;
5253
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.FORK_V9;
5354
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINE_STATS;
5455
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINE_STATS_SUPPORTS_REMOTE;
5556
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V12;
5657
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_PLANNING_V1;
5758
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.METADATA_FIELDS_REMOTE_TEST;
59+
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.RERANK;
5860
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND;
61+
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.TEXT_EMBEDDING_FUNCTION;
5962
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.UNMAPPED_FIELDS;
6063
import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.hasCapabilities;
6164
import static org.mockito.ArgumentMatchers.any;
@@ -81,6 +84,12 @@ public class MultiClusterSpecIT extends EsqlSpecTestCase {
8184
private static RestClient remoteClusterClient;
8285
private static DataLocation dataLocation = null;
8386

87+
private static final Set<String> LOCAL_ONLY_INFERENCE_CAPABILITIES = Set.of(
88+
RERANK.capabilityName(),
89+
COMPLETION.capabilityName(),
90+
TEXT_EMBEDDING_FUNCTION.capabilityName()
91+
);
92+
8493
@ParametersFactory(argumentFormatting = "csv-spec:%2$s.%3$s")
8594
public static List<Object[]> readScriptSpec() throws Exception {
8695
List<URL> urls = classpathResources("/*.csv-spec");
@@ -133,8 +142,15 @@ protected void shouldSkipTest(String testName) throws IOException {
133142
.filter(c -> c.equals("metadata_fields_remote_test") == false)
134143
.toList();
135144
}
145+
// Check all capabilities on the local cluster first.
136146
super.shouldSkipTest(testName);
137-
checkCapabilities(remoteClusterClient(), remoteFeaturesService(), testName, testCase);
147+
148+
// Filter out capabilities that are required only on the local cluster and then check the remaining on the remote cluster.
149+
List<String> remoteCapabilities = testCase.requiredCapabilities.stream()
150+
.filter(c -> LOCAL_ONLY_INFERENCE_CAPABILITIES.contains(c) == false)
151+
.toList();
152+
checkCapabilities(remoteClusterClient(), remoteFeaturesService(), testName, remoteCapabilities);
153+
138154
// Do not run tests including "METADATA _index" unless marked with metadata_fields_remote_test,
139155
// because they may produce inconsistent results with multiple clusters.
140156
assumeFalse("can't test with _index metadata", (remoteMetadata == false) && hasIndexMetadata(testCase.query));
@@ -224,10 +240,12 @@ protected RestClient buildClient(Settings settings, HttpHost[] localHosts) throw
224240
.collect(Collectors.toSet());
225241

226242
/**
227-
* Creates a new mock client that dispatches every request to both the local and remote clusters, excluding _bulk and _query requests.
243+
* Creates a new mock client that dispatches every request to both the local and remote clusters, excluding _bulk, _query,
244+
* and _inference requests :
228245
* - '_bulk' requests are randomly sent to either the local or remote cluster to populate data. Some spec tests, such as AVG,
229246
* prevent the splitting of bulk requests.
230247
* - '_query' requests are dispatched to the local cluster only, as we are testing cross-cluster queries.
248+
* - '_inference' requests are dispatched to the local cluster only, as inference endpoints are not available on remote clusters.
231249
*/
232250
static RestClient twoClients(RestClient localClient, RestClient remoteClient) throws IOException {
233251
RestClient twoClients = mock(RestClient.class);
@@ -239,6 +257,8 @@ static RestClient twoClients(RestClient localClient, RestClient remoteClient) th
239257
String endpoint = request.getEndpoint();
240258
if (endpoint.startsWith("/_query")) {
241259
return localClient.performRequest(request);
260+
} else if (endpoint.startsWith("/_inference")) {
261+
return localClient.performRequest(request);
242262
} else if (endpoint.endsWith("/_bulk") && METADATA_INDICES.stream().anyMatch(i -> endpoint.equals("/" + i + "/_bulk"))) {
243263
return remoteClient.performRequest(request);
244264
} else if (endpoint.endsWith("/_bulk")
@@ -338,6 +358,11 @@ protected boolean supportsInferenceTestService() {
338358
return false;
339359
}
340360

361+
@Override
362+
protected boolean supportsInferenceTestServiceOnLocalCluster() {
363+
return Clusters.localClusterSupportsInferenceTestService();
364+
}
365+
341366
@Override
342367
protected boolean supportsIndexModeLookup() throws IOException {
343368
return hasCapabilities(adminClient(), List.of(JOIN_LOOKUP_V12.capabilityName()));

x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushExpressionToLoadIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ protected boolean preserveClusterUponCompletion() {
823823

824824
private void setUpTextEmbeddingInferenceEndpoint() throws IOException {
825825
setupEmbeddings = true;
826-
Request request = new Request("PUT", "_inference/text_embedding/test");
826+
Request request = new Request("PUT", "/_inference/text_embedding/test");
827827
request.setJsonEntity("""
828828
{
829829
"service": "text_embedding_test_service",

x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ public void setUpTextEmbeddingInferenceEndpoint() throws IOException {
548548
return;
549549
}
550550
setupEmbeddings = true;
551-
Request request = new Request("PUT", "_inference/text_embedding/test");
551+
Request request = new Request("PUT", "/_inference/text_embedding/test");
552552
request.setJsonEntity("""
553553
{
554554
"service": "text_embedding_test_service",

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ public void setup() {
175175
assumeTrue("test clusters were broken", testClustersOk);
176176
INGEST.protectedBlock(() -> {
177177
// Inference endpoints must be created before ingesting any datasets that rely on them (mapping of inference_id)
178-
if (supportsInferenceTestService()) {
178+
// If multiple clusters are used, only create endpoints on the local cluster if it supports the inference test service.
179+
if (supportsInferenceTestServiceOnLocalCluster()) {
179180
createInferenceEndpoints(adminClient());
180181
}
181182
loadDataSetIntoEs(
@@ -239,6 +240,9 @@ protected void shouldSkipTest(String testName) throws IOException {
239240
if (requiresInferenceEndpoint()) {
240241
assumeTrue("Inference test service needs to be supported", supportsInferenceTestService());
241242
}
243+
if (requiresInferenceEndpointOnLocalCluster()) {
244+
assumeTrue("Inference test service needs to be supported", supportsInferenceTestServiceOnLocalCluster());
245+
}
242246
checkCapabilities(adminClient(), testFeatureService, testName, testCase);
243247
assumeTrue("Test " + testName + " is not enabled", isEnabled(testName, instructions, Version.CURRENT));
244248
if (supportsSourceFieldMapping() == false) {
@@ -252,13 +256,22 @@ protected static void checkCapabilities(
252256
String testName,
253257
CsvTestCase testCase
254258
) {
255-
if (hasCapabilities(client, testCase.requiredCapabilities)) {
259+
checkCapabilities(client, testFeatureService, testName, testCase.requiredCapabilities);
260+
}
261+
262+
protected static void checkCapabilities(
263+
RestClient client,
264+
TestFeatureService testFeatureService,
265+
String testName,
266+
List<String> requiredCapabilities
267+
) {
268+
if (hasCapabilities(client, requiredCapabilities)) {
256269
return;
257270
}
258271

259272
var features = new EsqlFeatures().getFeatures().stream().map(NodeFeature::id).collect(Collectors.toSet());
260273

261-
for (String feature : testCase.requiredCapabilities) {
274+
for (String feature : requiredCapabilities) {
262275
var esqlFeature = "esql." + feature;
263276
assumeTrue("Requested capability " + feature + " is an ESQL cluster feature", features.contains(esqlFeature));
264277
assumeTrue("Test " + testName + " requires " + feature, testFeatureService.clusterHasFeature(esqlFeature));
@@ -269,9 +282,16 @@ protected boolean supportsInferenceTestService() {
269282
return true;
270283
}
271284

285+
protected boolean supportsInferenceTestServiceOnLocalCluster() {
286+
return supportsInferenceTestService();
287+
}
288+
272289
protected boolean requiresInferenceEndpoint() {
290+
return testCase.requiredCapabilities.contains(SEMANTIC_TEXT_FIELD_CAPS.capabilityName());
291+
}
292+
293+
protected boolean requiresInferenceEndpointOnLocalCluster() {
273294
return Stream.of(
274-
SEMANTIC_TEXT_FIELD_CAPS.capabilityName(),
275295
RERANK.capabilityName(),
276296
COMPLETION.capabilityName(),
277297
KNN_FUNCTION_V5.capabilityName(),

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/SemanticMatchTestCase.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public void setUpIndices() throws IOException {
143143

144144
@Before
145145
public void setUpSparseEmbeddingInferenceEndpoint() throws IOException {
146-
Request request = new Request("PUT", "_inference/sparse_embedding/test_sparse_inference");
146+
Request request = new Request("PUT", "/_inference/sparse_embedding/test_sparse_inference");
147147
request.setJsonEntity("""
148148
{
149149
"service": "test_service",
@@ -165,7 +165,7 @@ public void setUpSparseEmbeddingInferenceEndpoint() throws IOException {
165165

166166
@Before
167167
public void setUpTextEmbeddingInferenceEndpoint() throws IOException {
168-
Request request = new Request("PUT", "_inference/text_embedding/test_dense_inference");
168+
Request request = new Request("PUT", "/_inference/text_embedding/test_dense_inference");
169169
request.setJsonEntity("""
170170
{
171171
"service": "text_embedding_test_service",
@@ -191,7 +191,7 @@ public void wipeData() throws IOException {
191191
adminClient().performRequest(new Request("DELETE", "*"));
192192

193193
try {
194-
adminClient().performRequest(new Request("DELETE", "_inference/test_sparse_inference"));
194+
adminClient().performRequest(new Request("DELETE", "/_inference/test_sparse_inference"));
195195
} catch (ResponseException e) {
196196
// 404 here means the endpoint was not created
197197
if (e.getResponse().getStatusLine().getStatusCode() != 404) {
@@ -200,7 +200,7 @@ public void wipeData() throws IOException {
200200
}
201201

202202
try {
203-
adminClient().performRequest(new Request("DELETE", "_inference/test_dense_inference"));
203+
adminClient().performRequest(new Request("DELETE", "/_inference/test_dense_inference"));
204204
} catch (ResponseException e) {
205205
// 404 here means the endpoint was not created
206206
if (e.getResponse().getStatusLine().getStatusCode() != 404) {

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestsDataLoader.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -623,13 +623,13 @@ public static boolean clusterHasCompletionInferenceEndpoint(RestClient client) t
623623

624624
private static void createInferenceEndpoint(RestClient client, TaskType taskType, String inferenceId, String modelSettings)
625625
throws IOException {
626-
Request request = new Request("PUT", "_inference/" + taskType.name() + "/" + inferenceId);
626+
Request request = new Request("PUT", "/_inference/" + taskType.name() + "/" + inferenceId);
627627
request.setJsonEntity(modelSettings);
628628
client.performRequest(request);
629629
}
630630

631631
private static boolean clusterHasInferenceEndpoint(RestClient client, TaskType taskType, String inferenceId) throws IOException {
632-
Request request = new Request("GET", "_inference/" + taskType.name() + "/" + inferenceId);
632+
Request request = new Request("GET", "/_inference/" + taskType.name() + "/" + inferenceId);
633633
try {
634634
client.performRequest(request);
635635
} catch (ResponseException e) {
@@ -643,7 +643,7 @@ private static boolean clusterHasInferenceEndpoint(RestClient client, TaskType t
643643

644644
private static void deleteInferenceEndpoint(RestClient client, String inferenceId) throws IOException {
645645
try {
646-
client.performRequest(new Request("DELETE", "_inference/" + inferenceId));
646+
client.performRequest(new Request("DELETE", "/_inference/" + inferenceId));
647647
} catch (ResponseException e) {
648648
// 404 here means the endpoint was not created
649649
if (e.getResponse().getStatusLine().getStatusCode() != 404) {

0 commit comments

Comments
 (0)