Skip to content

Commit 75228df

Browse files
Add granular error list to alias action response (#106514)
When an alias action list is posted with must_exist==false, and succeeds only partially, a list of results for each action are now returned. The results contain information about the requested action, indices, and aliases. If must_exist==true, or all actions fail, the call will return a 400 status along with the associated exception.
1 parent 24aed5c commit 75228df

File tree

26 files changed

+766
-58
lines changed

26 files changed

+766
-58
lines changed

docs/changelog/106514.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 106514
2+
summary: Add granular error list to alias action response
3+
area: Indices APIs
4+
type: feature
5+
issues:
6+
- 94478

docs/reference/alias.asciidoc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,77 @@ POST _aliases
121121
// TEST[s/^/PUT _data_stream\/logs-nginx.access-prod\nPUT _data_stream\/logs-my_app-default\n/]
122122
// end::alias-multiple-actions-example[]
123123

124+
[discrete]
125+
[[multiple-action-results]]
126+
=== Multiple action results
127+
128+
When using multiple actions, if some succeed and some fail, a list of per-action results will be returned.
129+
130+
Consider a similar action list to the previous example, but now with an alias `log-non-existing`, which does not yet exist.
131+
In this case, the `remove` action will fail, but the `add` action will succeed.
132+
The response will contain the list `action_results`, with a result for every requested action.
133+
134+
[source,console]
135+
----
136+
POST _aliases
137+
{
138+
"actions": [
139+
{
140+
"remove": {
141+
"index": "index1",
142+
"alias": "logs-non-existing"
143+
}
144+
},
145+
{
146+
"add": {
147+
"index": "index2",
148+
"alias": "logs-non-existing"
149+
}
150+
}
151+
]
152+
}
153+
----
154+
// TEST[s/^/PUT \/index1\nPUT \/index2\n/]
155+
156+
The API returns the following result:
157+
158+
[source,console-result]
159+
--------------------------------------------------
160+
{
161+
"acknowledged": true,
162+
"errors": true,
163+
"action_results": [
164+
{
165+
"action": {
166+
"type": "remove",
167+
"indices": [ "index1" ],
168+
"aliases": [ "logs-non-existing" ],
169+
},
170+
"status": 404,
171+
"error": {
172+
"type": "aliases_not_found_exception",
173+
"reason": "aliases [logs-non-existing] missing",
174+
"resource.type": "aliases",
175+
"resource.id": "logs-non-existing"
176+
}
177+
},
178+
{
179+
"action": {
180+
"type": "add",
181+
"indices": [ "index2" ],
182+
"aliases": [ "logs-non-existing" ],
183+
},
184+
"status": 200
185+
}
186+
]
187+
}
188+
--------------------------------------------------
189+
190+
Allowing the action list to succeed partially may not provide the desired result.
191+
It may be more appropriate to set `must_exist` to `true`, which will cause the entire action
192+
list to fail if a single action fails.
193+
194+
124195
[discrete]
125196
[[add-alias-at-creation]]
126197
=== Add an alias at index creation

docs/reference/indices/aliases.asciidoc

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,16 @@ the alias points to one data stream.
145145
+
146146
Only the `add` action supports this parameter.
147147

148+
// tag::alias-options[]
148149
`must_exist`::
149150
(Optional, Boolean)
150-
If `true`, the alias must exist to perform the action. Defaults to `false`. Only
151-
the `remove` action supports this parameter.
151+
Affects the behavior when attempting to remove an alias which does not exist.
152+
If `true`, removing an alias which does not exist will cause all actions to fail.
153+
If `false`, removing an alias which does not exist will only cause that removal to fail.
154+
Defaults to `false`.
155+
// end::alias-options[]
156+
+
157+
Only the `remove` action supports this parameter.
152158

153159
// tag::alias-options[]
154160
`routing`::
@@ -168,3 +174,51 @@ stream aliases don't support this parameter.
168174
Only the `add` action supports this parameter.
169175
=====
170176
====
177+
178+
179+
180+
[role="child_attributes"]
181+
[[indices-aliases-api-response-body]]
182+
==== {api-response-body-title}
183+
184+
`acknowledged`::
185+
(Boolean)
186+
If `true`, the request received a response from the master node within the
187+
`timeout` period.
188+
189+
`errors`::
190+
(Boolean)
191+
If `true`, at least one of the requested actions failed.
192+
193+
`action_results`::
194+
(Optional, array of objects) Results for each requested action.
195+
+
196+
.Properties of `action_results` objects
197+
[%collapsible%open]
198+
====
199+
200+
`action`::
201+
(object)
202+
Description of the associated action request.
203+
+
204+
.Properties of `action` object
205+
[%collapsible%open]
206+
=====
207+
`type`::
208+
(string) The type of the associated action, one of `add`, `remove`, or `remove_index`.
209+
210+
`indices`::
211+
(array of strings) List of indices in the associated action.
212+
213+
`aliases`::
214+
(array of strings) List of aliases in the associated action.
215+
=====
216+
217+
`status`::
218+
(integer) HTTP status code returned for the action.
219+
220+
`error`::
221+
(Optional, object) Contains additional information about the failed action.
222+
+
223+
Only present if the action failed.
224+
====

modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/140_data_stream_aliases.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,86 @@
307307
indices.get_alias:
308308
name: this-does-not-exist*
309309
- is_false: ds-first.aliases.my-alias
310+
---
311+
"Action Results with multiple matching aliases":
312+
- skip:
313+
version: " - 8.13.99"
314+
reason: "alias action results do not work until 8.14"
315+
features: allowed_warnings
316+
- do:
317+
allowed_warnings:
318+
- "index template [my-template] has index patterns [log-*] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-template] will take precedence during new index creation"
319+
indices.put_index_template:
320+
name: my-template
321+
body:
322+
index_patterns: [ log-* ]
323+
template:
324+
settings:
325+
index.number_of_replicas: 0
326+
data_stream: { }
327+
- do:
328+
indices.create_data_stream:
329+
name: log-foobar
330+
- is_true: acknowledged
331+
- do:
332+
indices.update_aliases:
333+
body:
334+
actions:
335+
- add:
336+
index: log-foobar
337+
aliases: test_alias1
338+
- remove:
339+
index: log-foobar
340+
aliases: test_non_existing
341+
must_exist: false
342+
- is_true: errors
343+
- length: { action_results: 2 }
344+
- match: { action_results.0.status: 200 }
345+
- match: { action_results.0.action: { 'type': 'add', 'indices': ['log-foobar'], 'aliases': ['test_alias1'] } }
346+
- match: { action_results.0.error: null }
347+
- match: { action_results.1.status: 404 }
348+
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['log-foobar'], 'aliases': ['test_non_existing'] } }
349+
- match: { action_results.1.error.type: aliases_not_found_exception }
350+
---
351+
"Single action result per action":
352+
- skip:
353+
version: " - 8.13.99"
354+
reason: "alias action results do not work until 8.14"
355+
features: allowed_warnings
356+
- do:
357+
allowed_warnings:
358+
- "index template [my-template] has index patterns [log-*] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-template] will take precedence during new index creation"
359+
indices.put_index_template:
360+
name: my-template
361+
body:
362+
index_patterns: [ log-* ]
363+
template:
364+
settings:
365+
index.number_of_replicas: 0
366+
data_stream: { }
367+
- do:
368+
indices.create_data_stream:
369+
name: log-test-1
370+
- do:
371+
indices.create_data_stream:
372+
name: log-test-2
373+
- is_true: acknowledged
374+
- do:
375+
indices.update_aliases:
376+
body:
377+
actions:
378+
- add:
379+
index: log-test-*
380+
aliases: test_alias1
381+
- remove:
382+
index: log-test-*
383+
aliases: test_non_existing
384+
must_exist: false
385+
- is_true: errors
386+
- length: { action_results: 2 }
387+
- match: { action_results.0.status: 200}
388+
- match: { action_results.0.action: { 'type': 'add', 'indices': ['log-test-1', 'log-test-2'], 'aliases': ['test_alias1'] } }
389+
- match: { action_results.0.error: null }
390+
- match: { action_results.1.status: 404 }
391+
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['log-test-1', 'log-test-2'], 'aliases': ['test_non_existing'] } }
392+
- match: { action_results.1.error.type: aliases_not_found_exception }

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.update_aliases/40_must_exist.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,100 @@
8282
- remove_index:
8383
index: test_index
8484
must_exist: true
85+
---
86+
"Partial success with must_exist == false":
87+
- skip:
88+
version: " - 8.13.99"
89+
reason: "alias action results do not work until 8.14"
90+
- do:
91+
indices.create:
92+
index: test_index
93+
- do:
94+
indices.update_aliases:
95+
body:
96+
actions:
97+
- add:
98+
index: test_index
99+
aliases: test_alias1
100+
- remove:
101+
index: test_index
102+
aliases: test_non_existing
103+
must_exist: false
104+
- is_true: errors
105+
- match: { action_results.0.status: 200 }
106+
- match: { action_results.0.action: { 'type': 'add', 'indices': ['test_index'], 'aliases': ['test_alias1'] } }
107+
- match: { action_results.0.error: null }
108+
- match: { action_results.1.status: 404 }
109+
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['test_index'], 'aliases': ['test_non_existing'] } }
110+
- match: { action_results.1.error.type: aliases_not_found_exception }
111+
---
112+
"Partial success with must_exist == null (default)":
113+
- skip:
114+
version: " - 8.13.99"
115+
reason: "alias action results do not work until 8.14"
116+
- do:
117+
indices.create:
118+
index: test_index
119+
- do:
120+
indices.update_aliases:
121+
body:
122+
actions:
123+
- add:
124+
index: test_index
125+
aliases: test_alias1
126+
- remove:
127+
index: test_index
128+
aliases: test_non_existing
129+
- is_true: errors
130+
- match: { action_results.0.status: 200}
131+
- match: { action_results.0.action: { 'type': 'add', 'indices': ['test_index'], 'aliases': ['test_alias1'] } }
132+
- match: { action_results.0.error: null }
133+
- match: { action_results.1.status: 404}
134+
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['test_index'], 'aliases': ['test_non_existing'] } }
135+
- match: { action_results.1.error.type: aliases_not_found_exception }
136+
---
137+
"No action_results field if all actions successful":
138+
- skip:
139+
version: " - 8.13.99"
140+
reason: "alias action results do not work until 8.14"
141+
- do:
142+
indices.create:
143+
index: test_index
144+
- do:
145+
indices.update_aliases:
146+
body:
147+
actions:
148+
- add:
149+
index: test_index
150+
aliases: test_alias1
151+
- is_false: errors
152+
- match: { action_results: null }
153+
---
154+
"Single result per input action":
155+
- skip:
156+
version: " - 8.13.99"
157+
reason: "alias action results do not work until 8.14"
158+
- do:
159+
indices.create:
160+
index: test_index1
161+
- do:
162+
indices.create:
163+
index: test_index2
164+
- do:
165+
indices.update_aliases:
166+
body:
167+
actions:
168+
- add:
169+
index: test_index*
170+
aliases: test_alias1
171+
- remove:
172+
index: test_index*
173+
aliases: test_non_existing
174+
- length: { action_results: 2 }
175+
- is_true: errors
176+
- match: { action_results.0.status: 200}
177+
- match: { action_results.0.action: { 'type': 'add', 'indices': ['test_index1', 'test_index2'], 'aliases': ['test_alias1'] } }
178+
- match: { action_results.0.error: null }
179+
- match: { action_results.1.status: 404}
180+
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['test_index1', 'test_index2'], 'aliases': ['test_non_existing'] } }
181+
- match: { action_results.1.error.type: aliases_not_found_exception }

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ static TransportVersion def(int id) {
164164
public static final TransportVersion ESQL_ORDINAL_BLOCK = def(8_623_00_0);
165165
public static final TransportVersion ML_INFERENCE_COHERE_RERANK = def(8_624_00_0);
166166
public static final TransportVersion INDEXING_PRESSURE_DOCUMENT_REJECTIONS_COUNT = def(8_625_00_0);
167+
public static final TransportVersion ALIAS_ACTION_RESULTS = def(8_626_00_0);
167168

168169
/*
169170
* STOP! READ THIS FIRST! No, really,

server/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesClusterStateUpdateRequest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
package org.elasticsearch.action.admin.indices.alias;
99

10+
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse.AliasActionResult;
1011
import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
1112
import org.elasticsearch.cluster.metadata.AliasAction;
1213

@@ -18,8 +19,11 @@
1819
public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest<IndicesAliasesClusterStateUpdateRequest> {
1920
private final List<AliasAction> actions;
2021

21-
public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions) {
22+
private final List<IndicesAliasesResponse.AliasActionResult> actionResults;
23+
24+
public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions, List<AliasActionResult> actionResults) {
2225
this.actions = actions;
26+
this.actionResults = actionResults;
2327
}
2428

2529
/**
@@ -28,4 +32,8 @@ public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions) {
2832
public List<AliasAction> actions() {
2933
return actions;
3034
}
35+
36+
public List<AliasActionResult> getActionResults() {
37+
return actionResults;
38+
}
3139
}

0 commit comments

Comments
 (0)