Skip to content

Commit 1b5827d

Browse files
Implement GET API for System Feature Upgrades (#78642) (#78860)
* Implement GET API for System Feature Upgrades (#78642) * Implement and test get feature upgrade status API * Add integration test for feature upgrade endpoint * Use constant enum for statuses * Add unit tests for transport class methods * Fix bwc tests for 7.x
1 parent 3b45d0a commit 1b5827d

File tree

9 files changed

+500
-64
lines changed

9 files changed

+500
-64
lines changed

client/rest-high-level/src/test/java/org/elasticsearch/client/MigrationIT.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package org.elasticsearch.client;
1010

1111
import org.elasticsearch.jdk.JavaVersion;
12+
import org.elasticsearch.Version;
1213
import org.elasticsearch.client.migration.DeprecationInfoRequest;
1314
import org.elasticsearch.client.migration.DeprecationInfoResponse;
1415
import org.elasticsearch.client.migration.GetFeatureUpgradeStatusRequest;
@@ -20,11 +21,14 @@
2021
import java.io.IOException;
2122
import java.util.Collections;
2223
import java.util.List;
24+
import java.util.Optional;
2325
import java.util.stream.Collectors;
2426

2527
import static org.hamcrest.Matchers.anEmptyMap;
2628
import static org.hamcrest.Matchers.empty;
2729
import static org.hamcrest.Matchers.equalTo;
30+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
31+
import static org.hamcrest.Matchers.is;
2832
import static org.hamcrest.Matchers.nullValue;
2933

3034
public class MigrationIT extends ESRestHighLevelClientTestCase {
@@ -51,19 +55,24 @@ public void testGetDeprecationInfo() throws IOException {
5155
public void testGetFeatureUpgradeStatus() throws IOException {
5256
GetFeatureUpgradeStatusRequest request = new GetFeatureUpgradeStatusRequest();
5357
GetFeatureUpgradeStatusResponse response = highLevelClient().migration().getFeatureUpgradeStatus(request, RequestOptions.DEFAULT);
54-
assertThat(response.getUpgradeStatus(), equalTo("UPGRADE_NEEDED"));
55-
assertThat(response.getFeatureUpgradeStatuses().size(), equalTo(1));
56-
GetFeatureUpgradeStatusResponse.FeatureUpgradeStatus status = response.getFeatureUpgradeStatuses().get(0);
57-
assertThat(status.getUpgradeStatus(), equalTo("UPGRADE_NEEDED"));
58-
assertThat(status.getMinimumIndexVersion(), equalTo("7.1.1"));
59-
assertThat(status.getFeatureName(), equalTo("security"));
60-
assertThat(status.getIndexVersions().size(), equalTo(1));
58+
assertThat(response.getUpgradeStatus(), equalTo("NO_UPGRADE_NEEDED"));
59+
assertThat(response.getFeatureUpgradeStatuses().size(), greaterThanOrEqualTo(1));
60+
Optional<GetFeatureUpgradeStatusResponse.FeatureUpgradeStatus> optionalTasksStatus = response.getFeatureUpgradeStatuses().stream()
61+
.filter(status -> "tasks".equals(status.getFeatureName()))
62+
.findFirst();
63+
64+
assertThat(optionalTasksStatus.isPresent(), is(true));
65+
66+
GetFeatureUpgradeStatusResponse.FeatureUpgradeStatus tasksStatus = optionalTasksStatus.get();
67+
68+
assertThat(tasksStatus.getUpgradeStatus(), equalTo("NO_UPGRADE_NEEDED"));
69+
assertThat(tasksStatus.getMinimumIndexVersion(), equalTo(Version.CURRENT.toString()));
70+
assertThat(tasksStatus.getFeatureName(), equalTo("tasks"));
6171
}
6272

6373
public void testPostFeatureUpgradeStatus() throws IOException {
6474
PostFeatureUpgradeRequest request = new PostFeatureUpgradeRequest();
6575
PostFeatureUpgradeResponse response = highLevelClient().migration().postFeatureUpgrade(request, RequestOptions.DEFAULT);
66-
// a test like this cannot test actual deprecations
6776
assertThat(response.isAccepted(), equalTo(true));
6877
assertThat(response.getFeatures().size(), equalTo(1));
6978
PostFeatureUpgradeResponse.Feature feature = response.getFeatures().get(0);

client/rest-high-level/src/test/java/org/elasticsearch/client/migration/GetFeatureUpgradeStatusResponseTests.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
package org.elasticsearch.client.migration;
1010

11+
import org.elasticsearch.Version;
1112
import org.elasticsearch.client.AbstractResponseTestCase;
1213
import org.elasticsearch.common.xcontent.XContentParser;
1314
import org.elasticsearch.common.xcontent.XContentType;
@@ -37,14 +38,14 @@ protected org.elasticsearch.action.admin.cluster.migration.GetFeatureUpgradeStat
3738
randomList(5,
3839
() -> new org.elasticsearch.action.admin.cluster.migration.GetFeatureUpgradeStatusResponse.FeatureUpgradeStatus(
3940
randomAlphaOfLengthBetween(3, 20),
40-
randomAlphaOfLengthBetween(5, 9),
41-
randomAlphaOfLengthBetween(4, 16),
41+
randomFrom(Version.CURRENT, Version.CURRENT.minimumCompatibilityVersion()),
42+
randomFrom(org.elasticsearch.action.admin.cluster.migration.GetFeatureUpgradeStatusResponse.UpgradeStatus.values()),
4243
randomList(4,
4344
() -> new org.elasticsearch.action.admin.cluster.migration.GetFeatureUpgradeStatusResponse.IndexVersion(
4445
randomAlphaOfLengthBetween(3, 20),
45-
randomAlphaOfLengthBetween(5, 9)))
46+
randomFrom(Version.CURRENT, Version.CURRENT.minimumCompatibilityVersion())))
4647
)),
47-
randomAlphaOfLength(5)
48+
randomFrom(org.elasticsearch.action.admin.cluster.migration.GetFeatureUpgradeStatusResponse.UpgradeStatus.values())
4849
);
4950
}
5051

@@ -58,7 +59,7 @@ protected void assertInstances(
5859
org.elasticsearch.action.admin.cluster.migration.GetFeatureUpgradeStatusResponse serverTestInstance,
5960
GetFeatureUpgradeStatusResponse clientInstance) {
6061

61-
assertThat(clientInstance.getUpgradeStatus(), equalTo(serverTestInstance.getUpgradeStatus()));
62+
assertThat(clientInstance.getUpgradeStatus(), equalTo(serverTestInstance.getUpgradeStatus().toString()));
6263

6364
assertNotNull(serverTestInstance.getFeatureUpgradeStatuses());
6465
assertNotNull(clientInstance.getFeatureUpgradeStatuses());
@@ -71,8 +72,8 @@ protected void assertInstances(
7172
GetFeatureUpgradeStatusResponse.FeatureUpgradeStatus clientStatus = clientInstance.getFeatureUpgradeStatuses().get(i);
7273

7374
assertThat(clientStatus.getFeatureName(), equalTo(serverTestStatus.getFeatureName()));
74-
assertThat(clientStatus.getMinimumIndexVersion(), equalTo(serverTestStatus.getMinimumIndexVersion()));
75-
assertThat(clientStatus.getUpgradeStatus(), equalTo(serverTestStatus.getUpgradeStatus()));
75+
assertThat(clientStatus.getMinimumIndexVersion(), equalTo(serverTestStatus.getMinimumIndexVersion().toString()));
76+
assertThat(clientStatus.getUpgradeStatus(), equalTo(serverTestStatus.getUpgradeStatus().toString()));
7677

7778
assertThat(clientStatus.getIndexVersions(), hasSize(serverTestStatus.getIndexVersions().size()));
7879

@@ -82,7 +83,7 @@ protected void assertInstances(
8283
GetFeatureUpgradeStatusResponse.IndexVersion clientIndexVersion = clientStatus.getIndexVersions().get(j);
8384

8485
assertThat(clientIndexVersion.getIndexName(), equalTo(serverIndexVersion.getIndexName()));
85-
assertThat(clientIndexVersion.getVersion(), equalTo(serverIndexVersion.getVersion()));
86+
assertThat(clientIndexVersion.getVersion(), equalTo(serverIndexVersion.getVersion().toString()));
8687
}
8788
}
8889
}

docs/reference/migration/apis/feature_upgrade.asciidoc

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,83 @@ Example response:
4343
--------------------------------------------------
4444
{
4545
"features" : [
46+
{
47+
"feature_name" : "async_search",
48+
"minimum_index_version" : "7.0.0",
49+
"upgrade_status" : "NO_UPGRADE_NEEDED",
50+
"indices" : [ ]
51+
},
52+
{
53+
"feature_name" : "enrich",
54+
"minimum_index_version" : "7.0.0",
55+
"upgrade_status" : "NO_UPGRADE_NEEDED",
56+
"indices" : [ ]
57+
},
58+
{
59+
"feature_name" : "fleet",
60+
"minimum_index_version" : "7.0.0",
61+
"upgrade_status" : "NO_UPGRADE_NEEDED",
62+
"indices" : [ ]
63+
},
64+
{
65+
"feature_name" : "geoip",
66+
"minimum_index_version" : "7.0.0",
67+
"upgrade_status" : "NO_UPGRADE_NEEDED",
68+
"indices" : [ ]
69+
},
70+
{
71+
"feature_name" : "kibana",
72+
"minimum_index_version" : "7.0.0",
73+
"upgrade_status" : "NO_UPGRADE_NEEDED",
74+
"indices" : [ ]
75+
},
76+
{
77+
"feature_name" : "logstash_management",
78+
"minimum_index_version" : "7.0.0",
79+
"upgrade_status" : "NO_UPGRADE_NEEDED",
80+
"indices" : [ ]
81+
},
82+
{
83+
"feature_name" : "machine_learning",
84+
"minimum_index_version" : "7.0.0",
85+
"upgrade_status" : "NO_UPGRADE_NEEDED",
86+
"indices" : [ ]
87+
},
88+
{
89+
"feature_name" : "searchable_snapshots",
90+
"minimum_index_version" : "7.0.0",
91+
"upgrade_status" : "NO_UPGRADE_NEEDED",
92+
"indices" : [ ]
93+
},
4694
{
4795
"feature_name" : "security",
48-
"minimum_index_version" : "7.1.1",
49-
"upgrade_status" : "UPGRADE_NEEDED",
50-
"indices" : [
51-
{
52-
"index" : ".security-7",
53-
"version" : "7.1.1"
54-
}
55-
]
96+
"minimum_index_version" : "7.0.0",
97+
"upgrade_status" : "NO_UPGRADE_NEEDED",
98+
"indices" : [ ]
99+
},
100+
{
101+
"feature_name" : "tasks",
102+
"minimum_index_version" : "7.0.0",
103+
"upgrade_status" : "NO_UPGRADE_NEEDED",
104+
"indices" : [ ]
105+
},
106+
{
107+
"feature_name" : "transform",
108+
"minimum_index_version" : "7.0.0",
109+
"upgrade_status" : "NO_UPGRADE_NEEDED",
110+
"indices" : [ ]
111+
},
112+
{
113+
"feature_name" : "watcher",
114+
"minimum_index_version" : "7.0.0",
115+
"upgrade_status" : "NO_UPGRADE_NEEDED",
116+
"indices" : [ ]
56117
}
57118
],
58-
"upgrade_status" : "UPGRADE_NEEDED"
119+
"upgrade_status" : "NO_UPGRADE_NEEDED"
59120
}
60121
--------------------------------------------------
122+
// TESTRESPONSE[s/"minimum_index_version" : "7.0.0"/"minimum_index_version" : $body.$_path/]
61123

62124
This response tells us that Elasticsearch security needs its internal
63125
indices upgraded before we can upgrade the cluster to 8.0.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.upgrades;
10+
11+
import org.elasticsearch.Version;
12+
import org.elasticsearch.action.admin.cluster.migration.TransportGetFeatureUpgradeStatusAction;
13+
import org.elasticsearch.client.Request;
14+
import org.elasticsearch.client.ResponseException;
15+
import org.elasticsearch.test.XContentTestUtils;
16+
17+
import java.util.Collections;
18+
import java.util.List;
19+
import java.util.Map;
20+
21+
import static org.hamcrest.Matchers.equalTo;
22+
import static org.hamcrest.Matchers.is;
23+
24+
public class FeatureUpgradeIT extends AbstractRollingTestCase {
25+
26+
@SuppressWarnings("unchecked")
27+
public void testGetFeatureUpgradeStatus() throws Exception {
28+
29+
final String systemIndexWarning = "this request accesses system indices: [.tasks], but in a future major version, direct " +
30+
"access to system indices will be prevented by default";
31+
if (CLUSTER_TYPE == ClusterType.OLD) {
32+
// setup - put something in the tasks index
33+
// create index
34+
Request createTestIndex = new Request("PUT", "/feature_test_index_old");
35+
createTestIndex.setJsonEntity("{\"settings\": {" +
36+
"\"index.number_of_replicas\": 0," +
37+
"\"index.number_of_shards\": 1" +
38+
"}}");
39+
client().performRequest(createTestIndex);
40+
41+
Request bulk = new Request("POST", "/_bulk");
42+
bulk.addParameter("refresh", "true");
43+
if (UPGRADE_FROM_VERSION.before(Version.V_7_0_0)) {
44+
bulk.setJsonEntity("{\"index\": {\"_index\": \"feature_test_index_old\", \"_type\" : \"_doc\"}}\n" +
45+
"{\"f1\": \"v1\", \"f2\": \"v2\"}\n");
46+
} else {
47+
bulk.setJsonEntity("{\"index\": {\"_index\": \"feature_test_index_old\"}\n" +
48+
"{\"f1\": \"v1\", \"f2\": \"v2\"}\n");
49+
}
50+
client().performRequest(bulk);
51+
52+
// start a async reindex job
53+
Request reindex = new Request("POST", "/_reindex");
54+
reindex.setJsonEntity(
55+
"{\n" +
56+
" \"source\":{\n" +
57+
" \"index\":\"feature_test_index_old\"\n" +
58+
" },\n" +
59+
" \"dest\":{\n" +
60+
" \"index\":\"feature_test_index_reindex\"\n" +
61+
" }\n" +
62+
"}");
63+
reindex.addParameter("wait_for_completion", "false");
64+
Map<String, Object> response = entityAsMap(client().performRequest(reindex));
65+
String taskId = (String) response.get("task");
66+
67+
// wait for task
68+
Request getTask = new Request("GET", "/_tasks/" + taskId);
69+
getTask.addParameter("wait_for_completion", "true");
70+
client().performRequest(getTask);
71+
72+
// make sure .tasks index exists
73+
Request getTasksIndex = new Request("GET", "/.tasks");
74+
getTasksIndex.addParameter("allow_no_indices", "false");
75+
if (UPGRADE_FROM_VERSION.before(Version.V_7_0_0)) {
76+
getTasksIndex.addParameter("include_type_name", "false");
77+
}
78+
79+
getTasksIndex.setOptions(expectVersionSpecificWarnings(v -> {
80+
v.current(systemIndexWarning);
81+
v.compatible(systemIndexWarning);
82+
}));
83+
assertBusy(() -> {
84+
try {
85+
assertThat(client().performRequest(getTasksIndex).getStatusLine().getStatusCode(), is(200));
86+
} catch (ResponseException e) {
87+
throw new AssertionError(".tasks index does not exist yet");
88+
}
89+
});
90+
91+
} else if (CLUSTER_TYPE == ClusterType.UPGRADED) {
92+
// check results
93+
assertBusy(() -> {
94+
Request clusterStateRequest = new Request("GET", "/_migration/system_features");
95+
XContentTestUtils.JsonMapView view = new XContentTestUtils.JsonMapView(
96+
entityAsMap(client().performRequest(clusterStateRequest)));
97+
98+
List<Map<String, Object>> features = view.get("features");
99+
Map<String, Object> feature = features.stream()
100+
.filter(e -> "tasks".equals(e.get("feature_name")))
101+
.findFirst()
102+
.orElse(Collections.emptyMap());
103+
104+
assertThat(feature.size(), equalTo(4));
105+
assertThat(feature.get("minimum_index_version"), equalTo(UPGRADE_FROM_VERSION.toString()));
106+
if (UPGRADE_FROM_VERSION.before(TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION)) {
107+
assertThat(feature.get("upgrade_status"), equalTo("UPGRADE_NEEDED"));
108+
} else {
109+
assertThat(feature.get("upgrade_status"), equalTo("NO_UPGRADE_NEEDED"));
110+
}
111+
});
112+
}
113+
}
114+
115+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.system.indices;
10+
11+
import org.elasticsearch.Version;
12+
import org.elasticsearch.client.Request;
13+
import org.elasticsearch.client.Response;
14+
import org.elasticsearch.common.settings.SecureString;
15+
import org.elasticsearch.common.settings.Settings;
16+
import org.elasticsearch.common.util.concurrent.ThreadContext;
17+
import org.elasticsearch.test.XContentTestUtils;
18+
import org.elasticsearch.test.rest.ESRestTestCase;
19+
import org.junit.After;
20+
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.Map;
24+
25+
import static org.hamcrest.Matchers.equalTo;
26+
import static org.hamcrest.Matchers.hasSize;
27+
import static org.hamcrest.Matchers.instanceOf;
28+
import static org.hamcrest.Matchers.is;
29+
30+
public class FeatureUpgradeApiIT extends ESRestTestCase {
31+
32+
static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("rest_user", new SecureString("rest-user-password".toCharArray()));
33+
34+
@After
35+
public void resetFeatures() throws Exception {
36+
client().performRequest(new Request("POST", "/_features/_reset"));
37+
}
38+
39+
@Override
40+
protected Settings restClientSettings() {
41+
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE).build();
42+
}
43+
44+
public void testCreatingSystemIndex() throws Exception {
45+
Response response = client().performRequest(new Request("PUT", "/_net_new_sys_index/_create"));
46+
assertThat(response.getStatusLine().getStatusCode(), is(200));
47+
}
48+
49+
@SuppressWarnings("unchecked")
50+
public void testGetFeatureUpgradedStatuses() throws Exception {
51+
client().performRequest(new Request("PUT", "/_net_new_sys_index/_create"));
52+
Response response = client().performRequest(new Request("GET", "/_migration/system_features"));
53+
assertThat(response.getStatusLine().getStatusCode(), is(200));
54+
XContentTestUtils.JsonMapView view = XContentTestUtils.createJsonMapView(response.getEntity().getContent());
55+
String upgradeStatus = view.get("upgrade_status");
56+
assertThat(upgradeStatus, equalTo("NO_UPGRADE_NEEDED"));
57+
List<Map<String, Object>> features = view.get("features");
58+
Map<String, Object> testFeature = features.stream()
59+
.filter(feature -> "system indices qa".equals(feature.get("feature_name")))
60+
.findFirst()
61+
.orElse(Collections.emptyMap());
62+
63+
assertThat(testFeature.size(), equalTo(4));
64+
assertThat(testFeature.get("minimum_index_version"), equalTo(Version.CURRENT.toString()));
65+
assertThat(testFeature.get("upgrade_status"), equalTo("NO_UPGRADE_NEEDED"));
66+
assertThat(testFeature.get("indices"), instanceOf(List.class));
67+
68+
assertThat((List<Object>) testFeature.get("indices"), hasSize(1));
69+
}
70+
}

0 commit comments

Comments
 (0)