Skip to content

Commit cb20776

Browse files
author
Ali Beyad
committed
Includes the index UUID in the _cat/indices API and adds tests
for the _cat/indices functionality. Closes #19204 Closes #19132
1 parent ff42d7c commit cb20776

File tree

4 files changed

+187
-7
lines changed

4 files changed

+187
-7
lines changed

core/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.elasticsearch.common.Table;
3939
import org.elasticsearch.common.inject.Inject;
4040
import org.elasticsearch.common.settings.Settings;
41+
import org.elasticsearch.index.Index;
4142
import org.elasticsearch.rest.RestChannel;
4243
import org.elasticsearch.rest.RestController;
4344
import org.elasticsearch.rest.RestRequest;
@@ -84,7 +85,7 @@ public void doRequest(final RestRequest request, final RestChannel channel, fina
8485
@Override
8586
public void processResponse(final ClusterStateResponse clusterStateResponse) {
8687
final ClusterState state = clusterStateResponse.getState();
87-
final String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, strictExpandIndicesOptions, indices);
88+
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, strictExpandIndicesOptions, indices);
8889
// concreteIndices should contain exactly the indices in state.metaData() that were selected by clusterStateRequest using
8990
// IndicesOptions.strictExpand(). We select the indices again here so that they can be displayed in the resulting table
9091
// in the requesting order.
@@ -129,6 +130,7 @@ protected Table getTableWithHeader(final RestRequest request) {
129130
table.addCell("health", "alias:h;desc:current health status");
130131
table.addCell("status", "alias:s;desc:open/close status");
131132
table.addCell("index", "alias:i,idx;desc:index name");
133+
table.addCell("uuid", "alias:id,uuid;desc:index uuid");
132134
table.addCell("pri", "alias:p,shards.primary,shardsPrimary;text-align:right;desc:number of primary shards");
133135
table.addCell("rep", "alias:r,shards.replica,shardsReplica;text-align:right;desc:number of replica shards");
134136
table.addCell("docs.count", "alias:dc,docsCount;text-align:right;desc:available docs");
@@ -312,19 +314,22 @@ protected Table getTableWithHeader(final RestRequest request) {
312314
return table;
313315
}
314316

315-
private Table buildTable(RestRequest request, String[] indices, ClusterHealthResponse health, IndicesStatsResponse stats, MetaData indexMetaDatas) {
317+
// package private for testing
318+
Table buildTable(RestRequest request, Index[] indices, ClusterHealthResponse health, IndicesStatsResponse stats, MetaData indexMetaDatas) {
316319
Table table = getTableWithHeader(request);
317320

318-
for (String index : indices) {
319-
ClusterIndexHealth indexHealth = health.getIndices().get(index);
320-
IndexStats indexStats = stats.getIndices().get(index);
321-
IndexMetaData indexMetaData = indexMetaDatas.getIndices().get(index);
321+
for (final Index index : indices) {
322+
final String indexName = index.getName();
323+
ClusterIndexHealth indexHealth = health.getIndices().get(indexName);
324+
IndexStats indexStats = stats.getIndices().get(indexName);
325+
IndexMetaData indexMetaData = indexMetaDatas.getIndices().get(indexName);
322326
IndexMetaData.State state = indexMetaData.getState();
323327

324328
table.startRow();
325329
table.addCell(state == IndexMetaData.State.OPEN ? (indexHealth == null ? "red*" : indexHealth.getStatus().toString().toLowerCase(Locale.ROOT)) : null);
326330
table.addCell(state.toString().toLowerCase(Locale.ROOT));
327-
table.addCell(index);
331+
table.addCell(indexName);
332+
table.addCell(index.getUUID());
328333
table.addCell(indexHealth == null ? null : indexHealth.getNumberOfShards());
329334
table.addCell(indexHealth == null ? null : indexHealth.getNumberOfReplicas());
330335
table.addCell(indexStats == null ? null : indexStats.getPrimaries().getDocs().getCount());

core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919

2020
package org.elasticsearch.action.admin.indices.stats;
2121

22+
import org.elasticsearch.action.ShardOperationFailedException;
2223
import org.elasticsearch.common.xcontent.XContentBuilder;
2324
import org.elasticsearch.common.xcontent.XContentFactory;
2425
import org.elasticsearch.index.engine.CommitStats;
2526
import org.elasticsearch.index.engine.SegmentsStats;
2627
import org.elasticsearch.index.translog.Translog;
2728
import org.elasticsearch.test.ESSingleNodeTestCase;
2829

30+
import java.util.List;
31+
2932
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
3033
import static org.hamcrest.Matchers.greaterThan;
3134
import static org.hamcrest.Matchers.hasKey;
@@ -108,4 +111,12 @@ public void testCommitStats() throws Exception {
108111
}
109112
}
110113

114+
/**
115+
* Gives access to package private IndicesStatsResponse constructor for test purpose.
116+
**/
117+
public static IndicesStatsResponse newIndicesStatsResponse(ShardStats[] shards, int totalShards, int successfulShards,
118+
int failedShards, List<ShardOperationFailedException> shardFailures) {
119+
return new IndicesStatsResponse(shards, totalShards, successfulShards, failedShards, shardFailures);
120+
}
121+
111122
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.rest.action.cat;
21+
22+
import org.elasticsearch.Version;
23+
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
24+
import org.elasticsearch.action.admin.indices.stats.CommonStats;
25+
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
26+
import org.elasticsearch.action.admin.indices.stats.IndicesStatsTests;
27+
import org.elasticsearch.action.admin.indices.stats.ShardStats;
28+
import org.elasticsearch.cluster.ClusterName;
29+
import org.elasticsearch.cluster.ClusterState;
30+
import org.elasticsearch.cluster.metadata.IndexMetaData;
31+
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
32+
import org.elasticsearch.cluster.metadata.MetaData;
33+
import org.elasticsearch.cluster.routing.ShardRouting;
34+
import org.elasticsearch.cluster.routing.UnassignedInfo;
35+
import org.elasticsearch.common.Table;
36+
import org.elasticsearch.common.UUIDs;
37+
import org.elasticsearch.common.settings.Settings;
38+
import org.elasticsearch.common.unit.TimeValue;
39+
import org.elasticsearch.index.Index;
40+
import org.elasticsearch.index.cache.query.QueryCacheStats;
41+
import org.elasticsearch.index.cache.request.RequestCacheStats;
42+
import org.elasticsearch.index.engine.SegmentsStats;
43+
import org.elasticsearch.index.fielddata.FieldDataStats;
44+
import org.elasticsearch.index.flush.FlushStats;
45+
import org.elasticsearch.index.get.GetStats;
46+
import org.elasticsearch.index.merge.MergeStats;
47+
import org.elasticsearch.index.refresh.RefreshStats;
48+
import org.elasticsearch.index.search.stats.SearchStats;
49+
import org.elasticsearch.index.shard.DocsStats;
50+
import org.elasticsearch.index.shard.IndexingStats;
51+
import org.elasticsearch.index.shard.ShardId;
52+
import org.elasticsearch.index.shard.ShardPath;
53+
import org.elasticsearch.index.store.StoreStats;
54+
import org.elasticsearch.index.warmer.WarmerStats;
55+
import org.elasticsearch.rest.RestController;
56+
import org.elasticsearch.search.suggest.completion.CompletionStats;
57+
import org.elasticsearch.test.ESTestCase;
58+
59+
import java.nio.file.Path;
60+
import java.util.ArrayList;
61+
import java.util.List;
62+
63+
import static java.util.Collections.emptyList;
64+
import static org.hamcrest.Matchers.equalTo;
65+
66+
/**
67+
* Tests for {@link RestIndicesAction}
68+
*/
69+
public class RestIndicesActionTests extends ESTestCase {
70+
71+
public void testBuildTable() {
72+
final Settings settings = Settings.EMPTY;
73+
final RestController restController = new RestController(settings);
74+
final RestIndicesAction action = new RestIndicesAction(settings, restController, new IndexNameExpressionResolver(settings));
75+
76+
// build a (semi-)random table
77+
final int numIndices = randomIntBetween(0, 5);
78+
Index[] indices = new Index[numIndices];
79+
for (int i = 0; i < numIndices; i++) {
80+
indices[i] = new Index(randomAsciiOfLength(5), UUIDs.randomBase64UUID());
81+
}
82+
83+
final MetaData.Builder metaDataBuilder = MetaData.builder();
84+
for (final Index index : indices) {
85+
metaDataBuilder.put(IndexMetaData.builder(index.getName())
86+
.settings(Settings.builder()
87+
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
88+
.put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID()))
89+
.creationDate(System.currentTimeMillis())
90+
.numberOfShards(1)
91+
.numberOfReplicas(1)
92+
.state(IndexMetaData.State.OPEN));
93+
}
94+
final MetaData metaData = metaDataBuilder.build();
95+
96+
final ClusterState clusterState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY))
97+
.metaData(metaData)
98+
.build();
99+
final String[] indicesStr = new String[indices.length];
100+
for (int i = 0; i < indices.length; i++) {
101+
indicesStr[i] = indices[i].getName();
102+
}
103+
final ClusterHealthResponse clusterHealth = new ClusterHealthResponse(
104+
clusterState.getClusterName().value(), indicesStr, clusterState, 0, 0, 0, TimeValue.timeValueMillis(1000L)
105+
);
106+
107+
final Table table = action.buildTable(null, indices, clusterHealth, randomIndicesStatsResponse(indices), metaData);
108+
109+
// now, verify the table is correct
110+
int count = 0;
111+
List<Table.Cell> headers = table.getHeaders();
112+
assertThat(headers.get(count++).value, equalTo("health"));
113+
assertThat(headers.get(count++).value, equalTo("status"));
114+
assertThat(headers.get(count++).value, equalTo("index"));
115+
assertThat(headers.get(count++).value, equalTo("uuid"));
116+
117+
List<List<Table.Cell>> rows = table.getRows();
118+
assertThat(rows.size(), equalTo(indices.length));
119+
// TODO: more to verify (e.g. randomize cluster health, num primaries, num replicas, etc)
120+
for (int i = 0; i < rows.size(); i++) {
121+
count = 0;
122+
final List<Table.Cell> row = rows.get(i);
123+
assertThat(row.get(count++).value, equalTo("red*")); // all are red because cluster state doesn't have routing entries
124+
assertThat(row.get(count++).value, equalTo("open")); // all are OPEN for now
125+
assertThat(row.get(count++).value, equalTo(indices[i].getName()));
126+
assertThat(row.get(count++).value, equalTo(indices[i].getUUID()));
127+
}
128+
}
129+
130+
private IndicesStatsResponse randomIndicesStatsResponse(final Index[] indices) {
131+
List<ShardStats> shardStats = new ArrayList<>();
132+
for (final Index index : indices) {
133+
for (int i = 0; i < 2; i++) {
134+
ShardId shardId = new ShardId(index, i);
135+
Path path = createTempDir().resolve("indices").resolve(index.getUUID()).resolve(String.valueOf(i));
136+
ShardRouting shardRouting = ShardRouting.newUnassigned(shardId, null, i == 0,
137+
new UnassignedInfo(UnassignedInfo.Reason.INDEX_CREATED, null));
138+
shardRouting = shardRouting.initialize("node-0", null, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE);
139+
shardRouting = shardRouting.moveToStarted();
140+
CommonStats stats = new CommonStats();
141+
stats.fieldData = new FieldDataStats();
142+
stats.queryCache = new QueryCacheStats();
143+
stats.docs = new DocsStats();
144+
stats.store = new StoreStats();
145+
stats.indexing = new IndexingStats();
146+
stats.search = new SearchStats();
147+
stats.segments = new SegmentsStats();
148+
stats.merge = new MergeStats();
149+
stats.refresh = new RefreshStats();
150+
stats.completion = new CompletionStats();
151+
stats.requestCache = new RequestCacheStats();
152+
stats.get = new GetStats();
153+
stats.flush = new FlushStats();
154+
stats.warmer = new WarmerStats();
155+
shardStats.add(new ShardStats(shardRouting, new ShardPath(false, path, path, shardId), stats, null));
156+
}
157+
}
158+
return IndicesStatsTests.newIndicesStatsResponse(
159+
shardStats.toArray(new ShardStats[shardStats.size()]), shardStats.size(), shardStats.size(), 0, emptyList()
160+
);
161+
}
162+
}

rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/^(green \s+
2727
open \s+
2828
index1 \s+
29+
([a-zA-Z0-9=/_+]|[\\\-]){22} \s+
2930
1 \s+
3031
0 \s+
3132
0 \s+
@@ -62,6 +63,7 @@
6263
/^( \s+
6364
close \s+
6465
index1 \s+
66+
([a-zA-Z0-9=/_+]|[\\\-]){22} \s+
6567
\s+
6668
\s+
6769
\s+

0 commit comments

Comments
 (0)