Skip to content

Commit d2f644f

Browse files
gaobinlongshiv0408
authored andcommitted
Enable must_exist parameter for update aliases API (opensearch-project#11210)
* Enable must_exist parameter for update aliases API Signed-off-by: Gao Binlong <[email protected]> * modify changelog Signed-off-by: Gao Binlong <[email protected]> * Fix test failure Signed-off-by: Gao Binlong <[email protected]> * Fix test failure Signed-off-by: Gao Binlong <[email protected]> * Remove silently when all aliases are non-existing and must_exist is false Signed-off-by: Gao Binlong <[email protected]> * Modify some comments to run gradle check again Signed-off-by: Gao Binlong <[email protected]> * Add more yaml test Signed-off-by: Gao Binlong <[email protected]> --------- Signed-off-by: Gao Binlong <[email protected]> Signed-off-by: Shivansh Arora <[email protected]>
1 parent dcb1230 commit d2f644f

File tree

8 files changed

+196
-9
lines changed

8 files changed

+196
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
107107
- Implement on behalf of token passing for extensions ([#8679](https://github.com/opensearch-project/OpenSearch/pull/8679))
108108
- Provide service accounts tokens to extensions ([#9618](https://github.com/opensearch-project/OpenSearch/pull/9618))
109109
- [Streaming Indexing] Introduce new experimental server HTTP transport based on Netty 4 and Project Reactor (Reactor Netty) ([#9672](https://github.com/opensearch-project/OpenSearch/pull/9672))
110+
- Enable must_exist parameter for update aliases API ([#11210](https://github.com/opensearch-project/OpenSearch/pull/11210))
110111
- Add back half_float BKD based sort query optimization ([#11024](https://github.com/opensearch-project/OpenSearch/pull/11024))
111112
- Request level coordinator slow logs ([#10650](https://github.com/opensearch-project/OpenSearch/pull/10650))
112113
- Add template snippets support for field and target_field in KV ingest processor ([#10040](https://github.com/opensearch-project/OpenSearch/pull/10040))
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
---
2+
"Throw aliases missing exception when removing non-existing alias with setting must_exist to true":
3+
- skip:
4+
version: " - 2.99.99"
5+
reason: "introduced in 3.0"
6+
7+
- do:
8+
indices.create:
9+
index: test_index
10+
11+
- do:
12+
indices.exists_alias:
13+
name: test_alias
14+
15+
- is_false: ''
16+
17+
- do:
18+
catch: /aliases \[test_alias\] missing/
19+
indices.update_aliases:
20+
body:
21+
actions:
22+
- remove:
23+
index: test_index
24+
alias: test_alias
25+
must_exist: true
26+
27+
- do:
28+
catch: /aliases \[testAlias\*\] missing/
29+
indices.update_aliases:
30+
body:
31+
actions:
32+
- remove:
33+
index: test_index
34+
aliases: [ testAlias* ]
35+
must_exist: true
36+
37+
- do:
38+
catch: /\[aliases\] can't be empty/
39+
indices.update_aliases:
40+
body:
41+
actions:
42+
- remove:
43+
index: test_index
44+
aliases: []
45+
must_exist: true
46+
47+
---
48+
"Throw aliases missing exception when all of the specified aliases are non-existing":
49+
- skip:
50+
version: " - 2.99.99"
51+
reason: "introduced in 3.0"
52+
53+
- do:
54+
indices.create:
55+
index: test_index
56+
57+
- do:
58+
indices.exists_alias:
59+
name: test_alias
60+
61+
- is_false: ''
62+
63+
- do:
64+
catch: /aliases \[test\_alias\] missing/
65+
indices.update_aliases:
66+
body:
67+
actions:
68+
- remove:
69+
index: test_index
70+
alias: test_alias
71+
72+
- do:
73+
catch: /aliases \[test\_alias\*\] missing/
74+
indices.update_aliases:
75+
body:
76+
actions:
77+
- remove:
78+
indices: [ test_index ]
79+
aliases: [ test_alias* ]
80+
81+
---
82+
"Remove successfully when some specified aliases are non-existing":
83+
- skip:
84+
version: " - 2.99.99"
85+
reason: "introduced in 3.0"
86+
87+
- do:
88+
indices.create:
89+
index: test_index
90+
91+
- do:
92+
indices.exists_alias:
93+
name: test_alias
94+
95+
- is_false: ''
96+
97+
- do:
98+
indices.update_aliases:
99+
body:
100+
actions:
101+
- add:
102+
indices: [ test_index ]
103+
aliases: [ test_alias ]
104+
105+
- do:
106+
indices.update_aliases:
107+
body:
108+
actions:
109+
- remove:
110+
index: test_index
111+
aliases: [test_alias, test_alias1, test_alias2]
112+
must_exist: false
113+
114+
- match: { acknowledged: true }
115+
116+
---
117+
"Remove silently when all of the specified aliases are non-existing and must_exist is false":
118+
- skip:
119+
version: " - 2.99.99"
120+
reason: "introduced in 3.0"
121+
122+
- do:
123+
indices.create:
124+
index: test_index
125+
126+
- do:
127+
indices.exists_alias:
128+
name: test_alias
129+
130+
- is_false: ''
131+
132+
- do:
133+
indices.update_aliases:
134+
body:
135+
actions:
136+
- remove:
137+
index: test_index
138+
aliases: [test_alias, test_alias1, test_alias2]
139+
must_exist: false
140+
141+
- match: { acknowledged: true }

server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequest.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,11 @@ private static ObjectParser<AliasActions, Void> parser(String name, Supplier<Ali
220220
ADD_PARSER.declareField(AliasActions::searchRouting, XContentParser::text, SEARCH_ROUTING, ValueType.INT);
221221
ADD_PARSER.declareField(AliasActions::writeIndex, XContentParser::booleanValue, IS_WRITE_INDEX, ValueType.BOOLEAN);
222222
ADD_PARSER.declareField(AliasActions::isHidden, XContentParser::booleanValue, IS_HIDDEN, ValueType.BOOLEAN);
223-
ADD_PARSER.declareField(AliasActions::mustExist, XContentParser::booleanValue, MUST_EXIST, ValueType.BOOLEAN);
224223
}
225224
private static final ObjectParser<AliasActions, Void> REMOVE_PARSER = parser(REMOVE.getPreferredName(), AliasActions::remove);
225+
static {
226+
REMOVE_PARSER.declareField(AliasActions::mustExist, XContentParser::booleanValue, MUST_EXIST, ValueType.BOOLEAN);
227+
}
226228
private static final ObjectParser<AliasActions, Void> REMOVE_INDEX_PARSER = parser(
227229
REMOVE_INDEX.getPreferredName(),
228230
AliasActions::removeIndex
@@ -554,6 +556,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
554556
if (null != isHidden) {
555557
builder.field(IS_HIDDEN.getPreferredName(), isHidden);
556558
}
559+
if (null != mustExist) {
560+
builder.field(MUST_EXIST.getPreferredName(), mustExist);
561+
}
557562
builder.endObject();
558563
builder.endObject();
559564
return builder;
@@ -582,6 +587,8 @@ public String toString() {
582587
+ searchRouting
583588
+ ",writeIndex="
584589
+ writeIndex
590+
+ ",mustExist="
591+
+ mustExist
585592
+ "]";
586593
}
587594

@@ -600,12 +607,13 @@ public boolean equals(Object obj) {
600607
&& Objects.equals(indexRouting, other.indexRouting)
601608
&& Objects.equals(searchRouting, other.searchRouting)
602609
&& Objects.equals(writeIndex, other.writeIndex)
603-
&& Objects.equals(isHidden, other.isHidden);
610+
&& Objects.equals(isHidden, other.isHidden)
611+
&& Objects.equals(mustExist, other.mustExist);
604612
}
605613

606614
@Override
607615
public int hashCode() {
608-
return Objects.hash(type, indices, aliases, filter, routing, indexRouting, searchRouting, writeIndex, isHidden);
616+
return Objects.hash(type, indices, aliases, filter, routing, indexRouting, searchRouting, writeIndex, isHidden, mustExist);
609617
}
610618
}
611619

server/src/main/java/org/opensearch/action/admin/indices/alias/TransportIndicesAliasesAction.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.opensearch.cluster.metadata.MetadataIndexAliasesService;
5151
import org.opensearch.cluster.service.ClusterService;
5252
import org.opensearch.common.inject.Inject;
53+
import org.opensearch.common.regex.Regex;
5354
import org.opensearch.core.action.ActionListener;
5455
import org.opensearch.core.common.io.stream.StreamInput;
5556
import org.opensearch.core.index.Index;
@@ -59,6 +60,7 @@
5960

6061
import java.io.IOException;
6162
import java.util.ArrayList;
63+
import java.util.Arrays;
6264
import java.util.Collections;
6365
import java.util.HashSet;
6466
import java.util.List;
@@ -220,12 +222,33 @@ private static String[] concreteAliases(IndicesAliasesRequest.AliasActions actio
220222
// for DELETE we expand the aliases
221223
String[] indexAsArray = { concreteIndex };
222224
final Map<String, List<AliasMetadata>> aliasMetadata = metadata.findAliases(action, indexAsArray);
223-
List<String> finalAliases = new ArrayList<>();
225+
Set<String> finalAliases = new HashSet<>();
224226
for (final List<AliasMetadata> curAliases : aliasMetadata.values()) {
225227
for (AliasMetadata aliasMeta : curAliases) {
226228
finalAliases.add(aliasMeta.alias());
227229
}
228230
}
231+
232+
// must_exist can only be set in the Remove Action in Update aliases API,
233+
// we check the value here to make the behavior consistent with Delete aliases API
234+
if (action.mustExist() != null) {
235+
// if must_exist is false, we should make the remove action execute silently,
236+
// so we return the original specified aliases to avoid AliasesNotFoundException
237+
if (!action.mustExist()) {
238+
return action.aliases();
239+
}
240+
241+
// if there is any non-existing aliases specified in the request and must_exist is true, throw exception in advance
242+
if (finalAliases.isEmpty()) {
243+
throw new AliasesNotFoundException(action.aliases());
244+
}
245+
String[] nonExistingAliases = Arrays.stream(action.aliases())
246+
.filter(originalAlias -> finalAliases.stream().noneMatch(finalAlias -> Regex.simpleMatch(originalAlias, finalAlias)))
247+
.toArray(String[]::new);
248+
if (nonExistingAliases.length != 0) {
249+
throw new AliasesNotFoundException(nonExistingAliases);
250+
}
251+
}
229252
return finalAliases.toArray(new String[0]);
230253
} else {
231254
// for ADD and REMOVE_INDEX we just return the current aliases

server/src/main/java/org/opensearch/cluster/metadata/AliasAction.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest;
3636
import org.opensearch.common.Nullable;
3737
import org.opensearch.core.common.Strings;
38+
import org.opensearch.rest.action.admin.indices.AliasesNotFoundException;
3839

3940
/**
4041
* Individual operation to perform on the cluster state as part of an {@link IndicesAliasesRequest}.
@@ -225,8 +226,8 @@ boolean removeIndex() {
225226
@Override
226227
boolean apply(NewAliasValidator aliasValidator, Metadata.Builder metadata, IndexMetadata index) {
227228
if (false == index.getAliases().containsKey(alias)) {
228-
if (mustExist != null && mustExist.booleanValue()) {
229-
throw new IllegalArgumentException("required alias [" + alias + "] does not exist");
229+
if (mustExist != null && mustExist) {
230+
throw new AliasesNotFoundException(alias);
230231
}
231232
return false;
232233
}

server/src/test/java/org/opensearch/action/admin/indices/alias/AliasActionsTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ public void testParseRemove() throws IOException {
241241
String[] indices = generateRandomStringArray(10, 5, false, false);
242242
String[] aliases = generateRandomStringArray(10, 5, false, false);
243243
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
244+
boolean mustExist = randomBoolean();
244245
b.startObject();
245246
{
246247
b.startObject("remove");
@@ -255,6 +256,9 @@ public void testParseRemove() throws IOException {
255256
} else {
256257
b.field("alias", aliases[0]);
257258
}
259+
if (mustExist) {
260+
b.field("must_exist", true);
261+
}
258262
}
259263
b.endObject();
260264
}
@@ -265,6 +269,9 @@ public void testParseRemove() throws IOException {
265269
assertEquals(AliasActions.Type.REMOVE, action.actionType());
266270
assertThat(action.indices(), equalTo(indices));
267271
assertThat(action.aliases(), equalTo(aliases));
272+
if (mustExist) {
273+
assertThat(action.mustExist(), equalTo(true));
274+
}
268275
}
269276
}
270277

server/src/test/java/org/opensearch/cluster/metadata/MetadataIndexAliasesServiceTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.opensearch.common.util.set.Sets;
4141
import org.opensearch.core.index.Index;
4242
import org.opensearch.index.IndexNotFoundException;
43+
import org.opensearch.rest.action.admin.indices.AliasesNotFoundException;
4344
import org.opensearch.test.OpenSearchTestCase;
4445
import org.opensearch.test.VersionUtils;
4546

@@ -164,11 +165,11 @@ public void testMustExist() {
164165

165166
// Show that removing non-existing alias with mustExist == true fails
166167
final ClusterState finalCS = after;
167-
final IllegalArgumentException iae = expectThrows(
168-
IllegalArgumentException.class,
168+
final AliasesNotFoundException iae = expectThrows(
169+
AliasesNotFoundException.class,
169170
() -> service.applyAliasActions(finalCS, singletonList(new AliasAction.Remove(index, "test_2", true)))
170171
);
171-
assertThat(iae.getMessage(), containsString("required alias [test_2] does not exist"));
172+
assertThat(iae.getMessage(), containsString("aliases [test_2] missing"));
172173
}
173174

174175
public void testMultipleIndices() {

test/framework/src/main/java/org/opensearch/index/alias/RandomAliasActionsGenerator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ public static AliasActions randomAliasAction(boolean useStringAsFilter) {
102102
action.isHidden(randomBoolean());
103103
}
104104
}
105+
if (action.actionType() == AliasActions.Type.REMOVE) {
106+
if (randomBoolean()) {
107+
action.mustExist(randomBoolean());
108+
}
109+
}
105110
return action;
106111
}
107112

0 commit comments

Comments
 (0)