mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-29 01:44:36 -04:00
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.
This commit is contained in:
parent
24aed5c7fe
commit
75228dfd45
26 changed files with 766 additions and 58 deletions
6
docs/changelog/106514.yaml
Normal file
6
docs/changelog/106514.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
pr: 106514
|
||||||
|
summary: Add granular error list to alias action response
|
||||||
|
area: Indices APIs
|
||||||
|
type: feature
|
||||||
|
issues:
|
||||||
|
- 94478
|
|
@ -121,6 +121,77 @@ POST _aliases
|
||||||
// TEST[s/^/PUT _data_stream\/logs-nginx.access-prod\nPUT _data_stream\/logs-my_app-default\n/]
|
// TEST[s/^/PUT _data_stream\/logs-nginx.access-prod\nPUT _data_stream\/logs-my_app-default\n/]
|
||||||
// end::alias-multiple-actions-example[]
|
// end::alias-multiple-actions-example[]
|
||||||
|
|
||||||
|
[discrete]
|
||||||
|
[[multiple-action-results]]
|
||||||
|
=== Multiple action results
|
||||||
|
|
||||||
|
When using multiple actions, if some succeed and some fail, a list of per-action results will be returned.
|
||||||
|
|
||||||
|
Consider a similar action list to the previous example, but now with an alias `log-non-existing`, which does not yet exist.
|
||||||
|
In this case, the `remove` action will fail, but the `add` action will succeed.
|
||||||
|
The response will contain the list `action_results`, with a result for every requested action.
|
||||||
|
|
||||||
|
[source,console]
|
||||||
|
----
|
||||||
|
POST _aliases
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"remove": {
|
||||||
|
"index": "index1",
|
||||||
|
"alias": "logs-non-existing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"add": {
|
||||||
|
"index": "index2",
|
||||||
|
"alias": "logs-non-existing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
----
|
||||||
|
// TEST[s/^/PUT \/index1\nPUT \/index2\n/]
|
||||||
|
|
||||||
|
The API returns the following result:
|
||||||
|
|
||||||
|
[source,console-result]
|
||||||
|
--------------------------------------------------
|
||||||
|
{
|
||||||
|
"acknowledged": true,
|
||||||
|
"errors": true,
|
||||||
|
"action_results": [
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"type": "remove",
|
||||||
|
"indices": [ "index1" ],
|
||||||
|
"aliases": [ "logs-non-existing" ],
|
||||||
|
},
|
||||||
|
"status": 404,
|
||||||
|
"error": {
|
||||||
|
"type": "aliases_not_found_exception",
|
||||||
|
"reason": "aliases [logs-non-existing] missing",
|
||||||
|
"resource.type": "aliases",
|
||||||
|
"resource.id": "logs-non-existing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"type": "add",
|
||||||
|
"indices": [ "index2" ],
|
||||||
|
"aliases": [ "logs-non-existing" ],
|
||||||
|
},
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
Allowing the action list to succeed partially may not provide the desired result.
|
||||||
|
It may be more appropriate to set `must_exist` to `true`, which will cause the entire action
|
||||||
|
list to fail if a single action fails.
|
||||||
|
|
||||||
|
|
||||||
[discrete]
|
[discrete]
|
||||||
[[add-alias-at-creation]]
|
[[add-alias-at-creation]]
|
||||||
=== Add an alias at index creation
|
=== Add an alias at index creation
|
||||||
|
|
|
@ -145,10 +145,16 @@ the alias points to one data stream.
|
||||||
+
|
+
|
||||||
Only the `add` action supports this parameter.
|
Only the `add` action supports this parameter.
|
||||||
|
|
||||||
|
// tag::alias-options[]
|
||||||
`must_exist`::
|
`must_exist`::
|
||||||
(Optional, Boolean)
|
(Optional, Boolean)
|
||||||
If `true`, the alias must exist to perform the action. Defaults to `false`. Only
|
Affects the behavior when attempting to remove an alias which does not exist.
|
||||||
the `remove` action supports this parameter.
|
If `true`, removing an alias which does not exist will cause all actions to fail.
|
||||||
|
If `false`, removing an alias which does not exist will only cause that removal to fail.
|
||||||
|
Defaults to `false`.
|
||||||
|
// end::alias-options[]
|
||||||
|
+
|
||||||
|
Only the `remove` action supports this parameter.
|
||||||
|
|
||||||
// tag::alias-options[]
|
// tag::alias-options[]
|
||||||
`routing`::
|
`routing`::
|
||||||
|
@ -168,3 +174,51 @@ stream aliases don't support this parameter.
|
||||||
Only the `add` action supports this parameter.
|
Only the `add` action supports this parameter.
|
||||||
=====
|
=====
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[role="child_attributes"]
|
||||||
|
[[indices-aliases-api-response-body]]
|
||||||
|
==== {api-response-body-title}
|
||||||
|
|
||||||
|
`acknowledged`::
|
||||||
|
(Boolean)
|
||||||
|
If `true`, the request received a response from the master node within the
|
||||||
|
`timeout` period.
|
||||||
|
|
||||||
|
`errors`::
|
||||||
|
(Boolean)
|
||||||
|
If `true`, at least one of the requested actions failed.
|
||||||
|
|
||||||
|
`action_results`::
|
||||||
|
(Optional, array of objects) Results for each requested action.
|
||||||
|
+
|
||||||
|
.Properties of `action_results` objects
|
||||||
|
[%collapsible%open]
|
||||||
|
====
|
||||||
|
|
||||||
|
`action`::
|
||||||
|
(object)
|
||||||
|
Description of the associated action request.
|
||||||
|
+
|
||||||
|
.Properties of `action` object
|
||||||
|
[%collapsible%open]
|
||||||
|
=====
|
||||||
|
`type`::
|
||||||
|
(string) The type of the associated action, one of `add`, `remove`, or `remove_index`.
|
||||||
|
|
||||||
|
`indices`::
|
||||||
|
(array of strings) List of indices in the associated action.
|
||||||
|
|
||||||
|
`aliases`::
|
||||||
|
(array of strings) List of aliases in the associated action.
|
||||||
|
=====
|
||||||
|
|
||||||
|
`status`::
|
||||||
|
(integer) HTTP status code returned for the action.
|
||||||
|
|
||||||
|
`error`::
|
||||||
|
(Optional, object) Contains additional information about the failed action.
|
||||||
|
+
|
||||||
|
Only present if the action failed.
|
||||||
|
====
|
||||||
|
|
|
@ -307,3 +307,86 @@
|
||||||
indices.get_alias:
|
indices.get_alias:
|
||||||
name: this-does-not-exist*
|
name: this-does-not-exist*
|
||||||
- is_false: ds-first.aliases.my-alias
|
- is_false: ds-first.aliases.my-alias
|
||||||
|
---
|
||||||
|
"Action Results with multiple matching aliases":
|
||||||
|
- skip:
|
||||||
|
version: " - 8.13.99"
|
||||||
|
reason: "alias action results do not work until 8.14"
|
||||||
|
features: allowed_warnings
|
||||||
|
- do:
|
||||||
|
allowed_warnings:
|
||||||
|
- "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"
|
||||||
|
indices.put_index_template:
|
||||||
|
name: my-template
|
||||||
|
body:
|
||||||
|
index_patterns: [ log-* ]
|
||||||
|
template:
|
||||||
|
settings:
|
||||||
|
index.number_of_replicas: 0
|
||||||
|
data_stream: { }
|
||||||
|
- do:
|
||||||
|
indices.create_data_stream:
|
||||||
|
name: log-foobar
|
||||||
|
- is_true: acknowledged
|
||||||
|
- do:
|
||||||
|
indices.update_aliases:
|
||||||
|
body:
|
||||||
|
actions:
|
||||||
|
- add:
|
||||||
|
index: log-foobar
|
||||||
|
aliases: test_alias1
|
||||||
|
- remove:
|
||||||
|
index: log-foobar
|
||||||
|
aliases: test_non_existing
|
||||||
|
must_exist: false
|
||||||
|
- is_true: errors
|
||||||
|
- length: { action_results: 2 }
|
||||||
|
- match: { action_results.0.status: 200 }
|
||||||
|
- match: { action_results.0.action: { 'type': 'add', 'indices': ['log-foobar'], 'aliases': ['test_alias1'] } }
|
||||||
|
- match: { action_results.0.error: null }
|
||||||
|
- match: { action_results.1.status: 404 }
|
||||||
|
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['log-foobar'], 'aliases': ['test_non_existing'] } }
|
||||||
|
- match: { action_results.1.error.type: aliases_not_found_exception }
|
||||||
|
---
|
||||||
|
"Single action result per action":
|
||||||
|
- skip:
|
||||||
|
version: " - 8.13.99"
|
||||||
|
reason: "alias action results do not work until 8.14"
|
||||||
|
features: allowed_warnings
|
||||||
|
- do:
|
||||||
|
allowed_warnings:
|
||||||
|
- "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"
|
||||||
|
indices.put_index_template:
|
||||||
|
name: my-template
|
||||||
|
body:
|
||||||
|
index_patterns: [ log-* ]
|
||||||
|
template:
|
||||||
|
settings:
|
||||||
|
index.number_of_replicas: 0
|
||||||
|
data_stream: { }
|
||||||
|
- do:
|
||||||
|
indices.create_data_stream:
|
||||||
|
name: log-test-1
|
||||||
|
- do:
|
||||||
|
indices.create_data_stream:
|
||||||
|
name: log-test-2
|
||||||
|
- is_true: acknowledged
|
||||||
|
- do:
|
||||||
|
indices.update_aliases:
|
||||||
|
body:
|
||||||
|
actions:
|
||||||
|
- add:
|
||||||
|
index: log-test-*
|
||||||
|
aliases: test_alias1
|
||||||
|
- remove:
|
||||||
|
index: log-test-*
|
||||||
|
aliases: test_non_existing
|
||||||
|
must_exist: false
|
||||||
|
- is_true: errors
|
||||||
|
- length: { action_results: 2 }
|
||||||
|
- match: { action_results.0.status: 200}
|
||||||
|
- match: { action_results.0.action: { 'type': 'add', 'indices': ['log-test-1', 'log-test-2'], 'aliases': ['test_alias1'] } }
|
||||||
|
- match: { action_results.0.error: null }
|
||||||
|
- match: { action_results.1.status: 404 }
|
||||||
|
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['log-test-1', 'log-test-2'], 'aliases': ['test_non_existing'] } }
|
||||||
|
- match: { action_results.1.error.type: aliases_not_found_exception }
|
||||||
|
|
|
@ -82,3 +82,100 @@
|
||||||
- remove_index:
|
- remove_index:
|
||||||
index: test_index
|
index: test_index
|
||||||
must_exist: true
|
must_exist: true
|
||||||
|
---
|
||||||
|
"Partial success with must_exist == false":
|
||||||
|
- skip:
|
||||||
|
version: " - 8.13.99"
|
||||||
|
reason: "alias action results do not work until 8.14"
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test_index
|
||||||
|
- do:
|
||||||
|
indices.update_aliases:
|
||||||
|
body:
|
||||||
|
actions:
|
||||||
|
- add:
|
||||||
|
index: test_index
|
||||||
|
aliases: test_alias1
|
||||||
|
- remove:
|
||||||
|
index: test_index
|
||||||
|
aliases: test_non_existing
|
||||||
|
must_exist: false
|
||||||
|
- is_true: errors
|
||||||
|
- match: { action_results.0.status: 200 }
|
||||||
|
- match: { action_results.0.action: { 'type': 'add', 'indices': ['test_index'], 'aliases': ['test_alias1'] } }
|
||||||
|
- match: { action_results.0.error: null }
|
||||||
|
- match: { action_results.1.status: 404 }
|
||||||
|
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['test_index'], 'aliases': ['test_non_existing'] } }
|
||||||
|
- match: { action_results.1.error.type: aliases_not_found_exception }
|
||||||
|
---
|
||||||
|
"Partial success with must_exist == null (default)":
|
||||||
|
- skip:
|
||||||
|
version: " - 8.13.99"
|
||||||
|
reason: "alias action results do not work until 8.14"
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test_index
|
||||||
|
- do:
|
||||||
|
indices.update_aliases:
|
||||||
|
body:
|
||||||
|
actions:
|
||||||
|
- add:
|
||||||
|
index: test_index
|
||||||
|
aliases: test_alias1
|
||||||
|
- remove:
|
||||||
|
index: test_index
|
||||||
|
aliases: test_non_existing
|
||||||
|
- is_true: errors
|
||||||
|
- match: { action_results.0.status: 200}
|
||||||
|
- match: { action_results.0.action: { 'type': 'add', 'indices': ['test_index'], 'aliases': ['test_alias1'] } }
|
||||||
|
- match: { action_results.0.error: null }
|
||||||
|
- match: { action_results.1.status: 404}
|
||||||
|
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['test_index'], 'aliases': ['test_non_existing'] } }
|
||||||
|
- match: { action_results.1.error.type: aliases_not_found_exception }
|
||||||
|
---
|
||||||
|
"No action_results field if all actions successful":
|
||||||
|
- skip:
|
||||||
|
version: " - 8.13.99"
|
||||||
|
reason: "alias action results do not work until 8.14"
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test_index
|
||||||
|
- do:
|
||||||
|
indices.update_aliases:
|
||||||
|
body:
|
||||||
|
actions:
|
||||||
|
- add:
|
||||||
|
index: test_index
|
||||||
|
aliases: test_alias1
|
||||||
|
- is_false: errors
|
||||||
|
- match: { action_results: null }
|
||||||
|
---
|
||||||
|
"Single result per input action":
|
||||||
|
- skip:
|
||||||
|
version: " - 8.13.99"
|
||||||
|
reason: "alias action results do not work until 8.14"
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test_index1
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test_index2
|
||||||
|
- do:
|
||||||
|
indices.update_aliases:
|
||||||
|
body:
|
||||||
|
actions:
|
||||||
|
- add:
|
||||||
|
index: test_index*
|
||||||
|
aliases: test_alias1
|
||||||
|
- remove:
|
||||||
|
index: test_index*
|
||||||
|
aliases: test_non_existing
|
||||||
|
- length: { action_results: 2 }
|
||||||
|
- is_true: errors
|
||||||
|
- match: { action_results.0.status: 200}
|
||||||
|
- match: { action_results.0.action: { 'type': 'add', 'indices': ['test_index1', 'test_index2'], 'aliases': ['test_alias1'] } }
|
||||||
|
- match: { action_results.0.error: null }
|
||||||
|
- match: { action_results.1.status: 404}
|
||||||
|
- match: { action_results.1.action: { 'type': 'remove', 'indices': ['test_index1', 'test_index2'], 'aliases': ['test_non_existing'] } }
|
||||||
|
- match: { action_results.1.error.type: aliases_not_found_exception }
|
||||||
|
|
|
@ -164,6 +164,7 @@ public class TransportVersions {
|
||||||
public static final TransportVersion ESQL_ORDINAL_BLOCK = def(8_623_00_0);
|
public static final TransportVersion ESQL_ORDINAL_BLOCK = def(8_623_00_0);
|
||||||
public static final TransportVersion ML_INFERENCE_COHERE_RERANK = def(8_624_00_0);
|
public static final TransportVersion ML_INFERENCE_COHERE_RERANK = def(8_624_00_0);
|
||||||
public static final TransportVersion INDEXING_PRESSURE_DOCUMENT_REJECTIONS_COUNT = def(8_625_00_0);
|
public static final TransportVersion INDEXING_PRESSURE_DOCUMENT_REJECTIONS_COUNT = def(8_625_00_0);
|
||||||
|
public static final TransportVersion ALIAS_ACTION_RESULTS = def(8_626_00_0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* STOP! READ THIS FIRST! No, really,
|
* STOP! READ THIS FIRST! No, really,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.action.admin.indices.alias;
|
package org.elasticsearch.action.admin.indices.alias;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse.AliasActionResult;
|
||||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
|
import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
|
||||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||||
|
|
||||||
|
@ -18,8 +19,11 @@ import java.util.List;
|
||||||
public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest<IndicesAliasesClusterStateUpdateRequest> {
|
public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest<IndicesAliasesClusterStateUpdateRequest> {
|
||||||
private final List<AliasAction> actions;
|
private final List<AliasAction> actions;
|
||||||
|
|
||||||
public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions) {
|
private final List<IndicesAliasesResponse.AliasActionResult> actionResults;
|
||||||
|
|
||||||
|
public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions, List<AliasActionResult> actionResults) {
|
||||||
this.actions = actions;
|
this.actions = actions;
|
||||||
|
this.actionResults = actionResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,4 +32,8 @@ public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateR
|
||||||
public List<AliasAction> actions() {
|
public List<AliasAction> actions() {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AliasActionResult> getActionResults() {
|
||||||
|
return actionResults;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,6 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
||||||
private static final ParseField IS_WRITE_INDEX = new ParseField("is_write_index");
|
private static final ParseField IS_WRITE_INDEX = new ParseField("is_write_index");
|
||||||
private static final ParseField IS_HIDDEN = new ParseField("is_hidden");
|
private static final ParseField IS_HIDDEN = new ParseField("is_hidden");
|
||||||
private static final ParseField MUST_EXIST = new ParseField("must_exist");
|
private static final ParseField MUST_EXIST = new ParseField("must_exist");
|
||||||
|
|
||||||
private static final ParseField ADD = new ParseField("add");
|
private static final ParseField ADD = new ParseField("add");
|
||||||
private static final ParseField REMOVE = new ParseField("remove");
|
private static final ParseField REMOVE = new ParseField("remove");
|
||||||
private static final ParseField REMOVE_INDEX = new ParseField("remove_index");
|
private static final ParseField REMOVE_INDEX = new ParseField("remove_index");
|
||||||
|
@ -105,6 +104,10 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFieldName() {
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
public static Type fromValue(byte value) {
|
public static Type fromValue(byte value) {
|
||||||
return switch (value) {
|
return switch (value) {
|
||||||
case 0 -> ADD;
|
case 0 -> ADD;
|
||||||
|
|
|
@ -10,7 +10,6 @@ package org.elasticsearch.action.admin.indices.alias;
|
||||||
|
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
|
||||||
import org.elasticsearch.client.internal.ElasticsearchClient;
|
import org.elasticsearch.client.internal.ElasticsearchClient;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<
|
public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<
|
||||||
IndicesAliasesRequest,
|
IndicesAliasesRequest,
|
||||||
AcknowledgedResponse,
|
IndicesAliasesResponse,
|
||||||
IndicesAliasesRequestBuilder> {
|
IndicesAliasesRequestBuilder> {
|
||||||
|
|
||||||
public IndicesAliasesRequestBuilder(ElasticsearchClient client) {
|
public IndicesAliasesRequestBuilder(ElasticsearchClient client) {
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.action.admin.indices.alias;
|
||||||
|
|
||||||
|
import org.elasticsearch.ElasticsearchException;
|
||||||
|
import org.elasticsearch.TransportVersions;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||||
|
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException;
|
||||||
|
import org.elasticsearch.xcontent.ToXContentObject;
|
||||||
|
import org.elasticsearch.xcontent.XContentBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response with error information for a request to add/remove aliases for one or more indices.
|
||||||
|
* Contains an acknowledged boolean, an errors boolean, and a list of results.
|
||||||
|
* The result list is only present if there are errors, and contains a result for every input action.
|
||||||
|
* This response replaces AcknowledgedResponse, and knows how to de/serialize from/to AcknowledgedResponse
|
||||||
|
* in case of mixed version clusters.
|
||||||
|
*/
|
||||||
|
public class IndicesAliasesResponse extends AcknowledgedResponse {
|
||||||
|
|
||||||
|
// Response without any error information, analogous to AcknowledgedResponse.FALSE
|
||||||
|
public static final IndicesAliasesResponse NOT_ACKNOWLEDGED = new IndicesAliasesResponse(false, false, List.of());
|
||||||
|
|
||||||
|
// Response without any error information, analogous to AcknowledgedResponse.TRUE
|
||||||
|
public static final IndicesAliasesResponse ACKNOWLEDGED_NO_ERRORS = new IndicesAliasesResponse(true, false, List.of());
|
||||||
|
|
||||||
|
private static final String ACTION_RESULTS_FIELD = "action_results";
|
||||||
|
private static final String ERRORS_FIELD = "errors";
|
||||||
|
|
||||||
|
private final List<AliasActionResult> actionResults;
|
||||||
|
private final boolean errors;
|
||||||
|
|
||||||
|
protected IndicesAliasesResponse(StreamInput in) throws IOException {
|
||||||
|
super(in);
|
||||||
|
|
||||||
|
if (in.getTransportVersion().onOrAfter(TransportVersions.ALIAS_ACTION_RESULTS)) {
|
||||||
|
this.errors = in.readBoolean();
|
||||||
|
this.actionResults = in.readCollectionAsImmutableList(AliasActionResult::new);
|
||||||
|
} else {
|
||||||
|
this.errors = false;
|
||||||
|
this.actionResults = List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param acknowledged whether the update was acknowledged by all the relevant nodes in the cluster
|
||||||
|
* @param errors true if any of the requested actions failed
|
||||||
|
* @param actionResults the list of results for each input action, only present if there are errors
|
||||||
|
*/
|
||||||
|
IndicesAliasesResponse(boolean acknowledged, boolean errors, final List<AliasActionResult> actionResults) {
|
||||||
|
super(acknowledged);
|
||||||
|
this.errors = errors;
|
||||||
|
this.actionResults = actionResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AliasActionResult> getActionResults() {
|
||||||
|
return actionResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a response from a list of action results. Sets the errors boolean based
|
||||||
|
* on whether an of the individual results contain an error.
|
||||||
|
* @param actionResults an action result for each of the requested alias actions
|
||||||
|
* @return response containing all action results
|
||||||
|
*/
|
||||||
|
public static IndicesAliasesResponse build(final List<AliasActionResult> actionResults) {
|
||||||
|
assert actionResults.isEmpty() == false : "IndicesAliasesResponse must be instantiated with at least one action result.";
|
||||||
|
final boolean errors = actionResults.stream().anyMatch(a -> a.error != null);
|
||||||
|
return new IndicesAliasesResponse(true, errors, actionResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
super.writeTo(out);
|
||||||
|
if (out.getTransportVersion().onOrAfter(TransportVersions.ALIAS_ACTION_RESULTS)) {
|
||||||
|
out.writeBoolean(errors);
|
||||||
|
out.writeCollection(actionResults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addCustomFields(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.field(ERRORS_FIELD, errors);
|
||||||
|
// if there are no errors, don't provide granular list of results
|
||||||
|
if (errors) {
|
||||||
|
builder.field(ACTION_RESULTS_FIELD, actionResults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Only used equals in tests
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
if (super.equals(o) == false) return false;
|
||||||
|
IndicesAliasesResponse response = (IndicesAliasesResponse) o;
|
||||||
|
return errors == response.errors && Objects.equals(actionResults, response.actionResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Only used hashCode in tests
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(super.hashCode(), actionResults, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result for a single alias add/remove action
|
||||||
|
*/
|
||||||
|
public static class AliasActionResult implements Writeable, ToXContentObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolved indices to which the action applies. This duplicates information
|
||||||
|
* which exists in the action, but is included because the action indices may
|
||||||
|
* or may not be resolved depending on if the security layer is used or not.
|
||||||
|
*/
|
||||||
|
private final List<String> indices;
|
||||||
|
private final AliasActions action;
|
||||||
|
private final ElasticsearchException error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build result that could be either a success or failure
|
||||||
|
* @param indices the resolved indices to which the associated action applies
|
||||||
|
* @param action the alias action consisting of add/remove, aliases, and indices
|
||||||
|
* @param numAliasesRemoved the number of aliases remove, if any
|
||||||
|
* @return the action result
|
||||||
|
*/
|
||||||
|
public static AliasActionResult build(List<String> indices, AliasActions action, int numAliasesRemoved) {
|
||||||
|
if (action.actionType() == AliasActions.Type.REMOVE && numAliasesRemoved == 0) {
|
||||||
|
return buildRemoveError(indices, action);
|
||||||
|
}
|
||||||
|
return buildSuccess(indices, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an error result for a failed remove action.
|
||||||
|
*/
|
||||||
|
private static AliasActionResult buildRemoveError(List<String> indices, AliasActions action) {
|
||||||
|
return new AliasActionResult(indices, action, new AliasesNotFoundException((action.getOriginalAliases())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a success action result with no errors.
|
||||||
|
*/
|
||||||
|
public static AliasActionResult buildSuccess(List<String> indices, AliasActions action) {
|
||||||
|
return new AliasActionResult(indices, action, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getStatus() {
|
||||||
|
return error == null ? 200 : error.status().getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AliasActionResult(List<String> indices, AliasActions action, ElasticsearchException error) {
|
||||||
|
assert indices.isEmpty() == false : "Alias action result must be instantiated with at least one index";
|
||||||
|
this.indices = indices;
|
||||||
|
this.action = action;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AliasActionResult(StreamInput in) throws IOException {
|
||||||
|
this.indices = in.readStringCollectionAsList();
|
||||||
|
this.action = new AliasActions(in);
|
||||||
|
this.error = in.readException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeStringCollection(indices);
|
||||||
|
action.writeTo(out);
|
||||||
|
out.writeException(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String ACTION_FIELD = "action";
|
||||||
|
public static final String ACTION_TYPE_FIELD = "type";
|
||||||
|
public static final String ACTION_INDICES_FIELD = "indices";
|
||||||
|
public static final String ACTION_ALIASES_FIELD = "aliases";
|
||||||
|
public static final String STATUS_FIELD = "status";
|
||||||
|
public static final String ERROR_FIELD = "error";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject();
|
||||||
|
|
||||||
|
// include subset of fields from action request
|
||||||
|
builder.field(ACTION_FIELD);
|
||||||
|
builder.startObject();
|
||||||
|
builder.field(ACTION_TYPE_FIELD, action.actionType().getFieldName());
|
||||||
|
builder.field(ACTION_INDICES_FIELD, indices.stream().sorted().collect(Collectors.toList()));
|
||||||
|
builder.array(ACTION_ALIASES_FIELD, action.getOriginalAliases());
|
||||||
|
builder.endObject();
|
||||||
|
|
||||||
|
builder.field(STATUS_FIELD, getStatus());
|
||||||
|
|
||||||
|
if (error != null) {
|
||||||
|
builder.startObject(ERROR_FIELD);
|
||||||
|
error.toXContent(builder, params);
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Only used equals in tests
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
AliasActionResult that = (AliasActionResult) o;
|
||||||
|
return Objects.equals(indices, that.indices) && Objects.equals(action, that.action)
|
||||||
|
// ElasticsearchException does not have hashCode() so assume errors are equal iff class and message are equal
|
||||||
|
&& Objects.equals(error == null ? null : error.getMessage(), that.error == null ? null : that.error.getMessage())
|
||||||
|
&& Objects.equals(error == null ? null : error.getClass(), that.error == null ? null : that.error.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Only used hashCode in tests
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(
|
||||||
|
indices,
|
||||||
|
action,
|
||||||
|
// ElasticsearchException does not have hashCode() so assume errors are equal iff class and message are equal
|
||||||
|
error == null ? null : error.getMessage(),
|
||||||
|
error == null ? null : error.getClass()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,9 @@ import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.ActionType;
|
import org.elasticsearch.action.ActionType;
|
||||||
import org.elasticsearch.action.RequestValidators;
|
import org.elasticsearch.action.RequestValidators;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse.AliasActionResult;
|
||||||
import org.elasticsearch.action.support.ActionFilters;
|
import org.elasticsearch.action.support.ActionFilters;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedTransportMasterNodeAction;
|
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||||
|
@ -56,10 +56,10 @@ import static java.util.Collections.unmodifiableList;
|
||||||
/**
|
/**
|
||||||
* Add/remove aliases action
|
* Add/remove aliases action
|
||||||
*/
|
*/
|
||||||
public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNodeAction<IndicesAliasesRequest> {
|
public class TransportIndicesAliasesAction extends TransportMasterNodeAction<IndicesAliasesRequest, IndicesAliasesResponse> {
|
||||||
|
|
||||||
public static final String NAME = "indices:admin/aliases";
|
public static final String NAME = "indices:admin/aliases";
|
||||||
public static final ActionType<AcknowledgedResponse> TYPE = new ActionType<>(NAME);
|
public static final ActionType<IndicesAliasesResponse> TYPE = new ActionType<>(NAME);
|
||||||
private static final Logger logger = LogManager.getLogger(TransportIndicesAliasesAction.class);
|
private static final Logger logger = LogManager.getLogger(TransportIndicesAliasesAction.class);
|
||||||
|
|
||||||
private final MetadataIndexAliasesService indexAliasesService;
|
private final MetadataIndexAliasesService indexAliasesService;
|
||||||
|
@ -85,6 +85,7 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
||||||
actionFilters,
|
actionFilters,
|
||||||
IndicesAliasesRequest::new,
|
IndicesAliasesRequest::new,
|
||||||
indexNameExpressionResolver,
|
indexNameExpressionResolver,
|
||||||
|
IndicesAliasesResponse::new,
|
||||||
EsExecutors.DIRECT_EXECUTOR_SERVICE
|
EsExecutors.DIRECT_EXECUTOR_SERVICE
|
||||||
);
|
);
|
||||||
this.indexAliasesService = indexAliasesService;
|
this.indexAliasesService = indexAliasesService;
|
||||||
|
@ -106,15 +107,19 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
||||||
Task task,
|
Task task,
|
||||||
final IndicesAliasesRequest request,
|
final IndicesAliasesRequest request,
|
||||||
final ClusterState state,
|
final ClusterState state,
|
||||||
final ActionListener<AcknowledgedResponse> listener
|
final ActionListener<IndicesAliasesResponse> listener
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Expand the indices names
|
// Expand the indices names
|
||||||
List<AliasActions> actions = request.aliasActions();
|
List<AliasActions> actions = request.aliasActions();
|
||||||
List<AliasAction> finalActions = new ArrayList<>();
|
List<AliasAction> finalActions = new ArrayList<>();
|
||||||
|
List<AliasActionResult> actionResults = new ArrayList<>();
|
||||||
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
||||||
Set<String> aliases = new HashSet<>();
|
Set<String> aliases = new HashSet<>();
|
||||||
for (AliasActions action : actions) {
|
for (AliasActions action : actions) {
|
||||||
|
int numAliasesRemoved = 0;
|
||||||
|
List<String> resolvedIndices = new ArrayList<>();
|
||||||
|
|
||||||
List<String> concreteDataStreams = indexNameExpressionResolver.dataStreamNames(
|
List<String> concreteDataStreams = indexNameExpressionResolver.dataStreamNames(
|
||||||
state,
|
state,
|
||||||
request.indicesOptions(),
|
request.indicesOptions(),
|
||||||
|
@ -161,18 +166,24 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
||||||
finalActions.add(new AddDataStreamAlias(alias, dataStreamName, action.writeIndex(), action.filter()));
|
finalActions.add(new AddDataStreamAlias(alias, dataStreamName, action.writeIndex(), action.filter()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actionResults.add(AliasActionResult.buildSuccess(concreteDataStreams, action));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case REMOVE -> {
|
case REMOVE -> {
|
||||||
for (String dataStreamName : concreteDataStreams) {
|
for (String dataStreamName : concreteDataStreams) {
|
||||||
for (String alias : concreteDataStreamAliases(action, state.metadata(), dataStreamName)) {
|
for (String alias : concreteDataStreamAliases(action, state.metadata(), dataStreamName)) {
|
||||||
finalActions.add(new AliasAction.RemoveDataStreamAlias(alias, dataStreamName, action.mustExist()));
|
finalActions.add(new AliasAction.RemoveDataStreamAlias(alias, dataStreamName, action.mustExist()));
|
||||||
|
numAliasesRemoved++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonBackingIndices.isEmpty() == false) {
|
if (nonBackingIndices.isEmpty() == false) {
|
||||||
// Regular aliases/indices match as well with the provided expression.
|
// Regular aliases/indices match as well with the provided expression.
|
||||||
// (Only when adding new aliases, matching both data streams and indices is disallowed)
|
// (Only when adding new aliases, matching both data streams and indices is disallowed)
|
||||||
|
resolvedIndices.addAll(concreteDataStreams);
|
||||||
} else {
|
} else {
|
||||||
|
actionResults.add(AliasActionResult.build(concreteDataStreams, action, numAliasesRemoved));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +235,7 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
for (String alias : concreteAliases(action, state.metadata(), index.getName())) {
|
for (String alias : concreteAliases(action, state.metadata(), index.getName())) {
|
||||||
finalActions.add(new AliasAction.Remove(index.getName(), alias, action.mustExist()));
|
finalActions.add(new AliasAction.Remove(index.getName(), alias, action.mustExist()));
|
||||||
|
numAliasesRemoved++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REMOVE_INDEX:
|
case REMOVE_INDEX:
|
||||||
|
@ -233,14 +245,18 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
||||||
throw new IllegalArgumentException("Unsupported action [" + action.actionType() + "]");
|
throw new IllegalArgumentException("Unsupported action [" + action.actionType() + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Arrays.stream(concreteIndices).map(Index::getName).forEach(resolvedIndices::add);
|
||||||
|
actionResults.add(AliasActionResult.build(resolvedIndices, action, numAliasesRemoved));
|
||||||
}
|
}
|
||||||
if (finalActions.isEmpty() && false == actions.isEmpty()) {
|
if (finalActions.isEmpty() && false == actions.isEmpty()) {
|
||||||
throw new AliasesNotFoundException(aliases.toArray(new String[aliases.size()]));
|
throw new AliasesNotFoundException(aliases.toArray(new String[aliases.size()]));
|
||||||
}
|
}
|
||||||
request.aliasActions().clear();
|
request.aliasActions().clear();
|
||||||
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(unmodifiableList(finalActions))
|
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(
|
||||||
.ackTimeout(request.timeout())
|
unmodifiableList(finalActions),
|
||||||
.masterNodeTimeout(request.masterNodeTimeout());
|
unmodifiableList(actionResults)
|
||||||
|
).ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout());
|
||||||
|
|
||||||
indexAliasesService.indicesAliases(updateRequest, listener.delegateResponse((l, e) -> {
|
indexAliasesService.indicesAliases(updateRequest, listener.delegateResponse((l, e) -> {
|
||||||
logger.debug("failed to perform aliases", e);
|
logger.debug("failed to perform aliases", e);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.action.ActionFuture;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequestBuilder;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||||
|
@ -371,7 +372,7 @@ public interface IndicesAdminClient extends ElasticsearchClient {
|
||||||
* @param request The index aliases request
|
* @param request The index aliases request
|
||||||
* @return The result future
|
* @return The result future
|
||||||
*/
|
*/
|
||||||
ActionFuture<AcknowledgedResponse> aliases(IndicesAliasesRequest request);
|
ActionFuture<IndicesAliasesResponse> aliases(IndicesAliasesRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to add/remove aliases from indices.
|
* Allows to add/remove aliases from indices.
|
||||||
|
@ -379,7 +380,7 @@ public interface IndicesAdminClient extends ElasticsearchClient {
|
||||||
* @param request The index aliases request
|
* @param request The index aliases request
|
||||||
* @param listener A listener to be notified with a result
|
* @param listener A listener to be notified with a result
|
||||||
*/
|
*/
|
||||||
void aliases(IndicesAliasesRequest request, ActionListener<AcknowledgedResponse> listener);
|
void aliases(IndicesAliasesRequest request, ActionListener<IndicesAliasesResponse> listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to add/remove aliases from indices.
|
* Allows to add/remove aliases from indices.
|
||||||
|
|
|
@ -118,6 +118,7 @@ import org.elasticsearch.action.admin.cluster.storedscripts.TransportDeleteStore
|
||||||
import org.elasticsearch.action.admin.cluster.storedscripts.TransportPutStoredScriptAction;
|
import org.elasticsearch.action.admin.cluster.storedscripts.TransportPutStoredScriptAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
|
import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||||
|
@ -1083,12 +1084,12 @@ public abstract class AbstractClient implements Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActionFuture<AcknowledgedResponse> aliases(final IndicesAliasesRequest request) {
|
public ActionFuture<IndicesAliasesResponse> aliases(final IndicesAliasesRequest request) {
|
||||||
return execute(TransportIndicesAliasesAction.TYPE, request);
|
return execute(TransportIndicesAliasesAction.TYPE, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void aliases(final IndicesAliasesRequest request, final ActionListener<AcknowledgedResponse> listener) {
|
public void aliases(final IndicesAliasesRequest request, final ActionListener<IndicesAliasesResponse> listener) {
|
||||||
execute(TransportIndicesAliasesAction.TYPE, request, listener);
|
execute(TransportIndicesAliasesAction.TYPE, request, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
package org.elasticsearch.cluster.metadata;
|
package org.elasticsearch.cluster.metadata;
|
||||||
|
|
||||||
import org.elasticsearch.ResourceNotFoundException;
|
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.core.Nullable;
|
import org.elasticsearch.core.Nullable;
|
||||||
|
import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Individual operation to perform on the cluster state as part of an {@link IndicesAliasesRequest}.
|
* Individual operation to perform on the cluster state as part of an {@link IndicesAliasesRequest}.
|
||||||
|
@ -189,7 +189,7 @@ public abstract class AliasAction {
|
||||||
boolean apply(NewAliasValidator aliasValidator, Metadata.Builder metadata, IndexMetadata index) {
|
boolean apply(NewAliasValidator aliasValidator, Metadata.Builder metadata, IndexMetadata index) {
|
||||||
if (false == index.getAliases().containsKey(alias)) {
|
if (false == index.getAliases().containsKey(alias)) {
|
||||||
if (mustExist != null && mustExist) {
|
if (mustExist != null && mustExist) {
|
||||||
throw new ResourceNotFoundException("required alias [" + alias + "] does not exist");
|
throw new AliasesNotFoundException(alias);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ package org.elasticsearch.cluster.metadata;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateAckListener;
|
import org.elasticsearch.cluster.ClusterStateAckListener;
|
||||||
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
||||||
|
@ -79,7 +79,10 @@ public class MetadataIndexAliasesService {
|
||||||
this.taskQueue = clusterService.createTaskQueue("index-aliases", Priority.URGENT, this.executor);
|
this.taskQueue = clusterService.createTaskQueue("index-aliases", Priority.URGENT, this.executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void indicesAliases(final IndicesAliasesClusterStateUpdateRequest request, final ActionListener<AcknowledgedResponse> listener) {
|
public void indicesAliases(
|
||||||
|
final IndicesAliasesClusterStateUpdateRequest request,
|
||||||
|
final ActionListener<IndicesAliasesResponse> listener
|
||||||
|
) {
|
||||||
taskQueue.submitTask("index-aliases", new ApplyAliasesTask(request, listener), null); // TODO use request.masterNodeTimeout() here?
|
taskQueue.submitTask("index-aliases", new ApplyAliasesTask(request, listener), null); // TODO use request.masterNodeTimeout() here?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +257,7 @@ public class MetadataIndexAliasesService {
|
||||||
/**
|
/**
|
||||||
* A cluster state update task that consists of the cluster state request and the listeners that need to be notified upon completion.
|
* A cluster state update task that consists of the cluster state request and the listeners that need to be notified upon completion.
|
||||||
*/
|
*/
|
||||||
record ApplyAliasesTask(IndicesAliasesClusterStateUpdateRequest request, ActionListener<AcknowledgedResponse> listener)
|
record ApplyAliasesTask(IndicesAliasesClusterStateUpdateRequest request, ActionListener<IndicesAliasesResponse> listener)
|
||||||
implements
|
implements
|
||||||
ClusterStateTaskListener,
|
ClusterStateTaskListener,
|
||||||
ClusterStateAckListener {
|
ClusterStateAckListener {
|
||||||
|
@ -271,17 +274,17 @@ public class MetadataIndexAliasesService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAllNodesAcked() {
|
public void onAllNodesAcked() {
|
||||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
listener.onResponse(IndicesAliasesResponse.build(request.getActionResults()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAckFailure(Exception e) {
|
public void onAckFailure(Exception e) {
|
||||||
listener.onResponse(AcknowledgedResponse.FALSE);
|
listener.onResponse(IndicesAliasesResponse.NOT_ACKNOWLEDGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAckTimeout() {
|
public void onAckTimeout() {
|
||||||
listener.onResponse(AcknowledgedResponse.FALSE);
|
listener.onResponse(IndicesAliasesResponse.NOT_ACKNOWLEDGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.action.admin.indices.alias;
|
||||||
|
|
||||||
|
import org.elasticsearch.TransportVersions;
|
||||||
|
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||||
|
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.index.alias.RandomAliasActionsGenerator;
|
||||||
|
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IndicesAliasesResponseTests extends AbstractWireSerializingTestCase<IndicesAliasesResponse> {
|
||||||
|
public void testMixedModeSerialization() throws IOException {
|
||||||
|
|
||||||
|
// AcknowledgedResponse to IndicesAliasesResponse
|
||||||
|
// in version before TransportVersions.ALIAS_ACTION_RESULTS
|
||||||
|
{
|
||||||
|
var ack = AcknowledgedResponse.of(randomBoolean());
|
||||||
|
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||||
|
ack.writeTo(output);
|
||||||
|
try (StreamInput in = output.bytes().streamInput()) {
|
||||||
|
in.setTransportVersion(TransportVersions.V_8_12_0);
|
||||||
|
|
||||||
|
var indicesAliasesResponse = new IndicesAliasesResponse(in);
|
||||||
|
|
||||||
|
assertEquals(ack.isAcknowledged(), indicesAliasesResponse.isAcknowledged());
|
||||||
|
assertTrue(indicesAliasesResponse.getActionResults().isEmpty());
|
||||||
|
assertFalse(indicesAliasesResponse.hasErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndicesAliasesResponse to AcknowledgedResponse
|
||||||
|
// out version before TransportVersions.ALIAS_ACTION_RESULTS
|
||||||
|
{
|
||||||
|
var indicesAliasesResponse = randomIndicesAliasesResponse();
|
||||||
|
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||||
|
output.setTransportVersion(TransportVersions.V_8_12_0);
|
||||||
|
|
||||||
|
indicesAliasesResponse.writeTo(output);
|
||||||
|
try (StreamInput in = output.bytes().streamInput()) {
|
||||||
|
var ack = AcknowledgedResponse.readFrom(in);
|
||||||
|
assertEquals(ack.isAcknowledged(), indicesAliasesResponse.isAcknowledged());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Writeable.Reader<IndicesAliasesResponse> instanceReader() {
|
||||||
|
return IndicesAliasesResponse::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IndicesAliasesResponse createTestInstance() {
|
||||||
|
return randomIndicesAliasesResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IndicesAliasesResponse randomIndicesAliasesResponse() {
|
||||||
|
int numActions = between(0, 5);
|
||||||
|
List<IndicesAliasesResponse.AliasActionResult> results = new ArrayList<>();
|
||||||
|
for (int i = 0; i < numActions; ++i) {
|
||||||
|
results.add(randomIndicesAliasesResult());
|
||||||
|
}
|
||||||
|
return new IndicesAliasesResponse(randomBoolean(), randomBoolean(), results);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IndicesAliasesResponse mutateInstance(IndicesAliasesResponse instance) throws IOException {
|
||||||
|
switch (between(0, 2)) {
|
||||||
|
case 0: {
|
||||||
|
boolean acknowledged = instance.isAcknowledged() == false;
|
||||||
|
return new IndicesAliasesResponse(acknowledged, instance.hasErrors(), instance.getActionResults());
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
boolean errors = instance.hasErrors() == false;
|
||||||
|
return new IndicesAliasesResponse(instance.isAcknowledged(), errors, instance.getActionResults());
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
var results = new ArrayList<>(instance.getActionResults());
|
||||||
|
if (results.isEmpty()) {
|
||||||
|
results.add(randomIndicesAliasesResult());
|
||||||
|
} else {
|
||||||
|
results.remove(between(0, results.size() - 1));
|
||||||
|
}
|
||||||
|
return new IndicesAliasesResponse(instance.isAcknowledged(), instance.hasErrors(), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IndicesAliasesResponse.AliasActionResult randomIndicesAliasesResult() {
|
||||||
|
var action = RandomAliasActionsGenerator.randomAliasAction();
|
||||||
|
var indices = Arrays.asList(generateRandomStringArray(10, 5, false, false));
|
||||||
|
return IndicesAliasesResponse.AliasActionResult.build(indices, action, randomIntBetween(0, 3));
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,8 +8,9 @@
|
||||||
|
|
||||||
package org.elasticsearch.cluster.metadata;
|
package org.elasticsearch.cluster.metadata;
|
||||||
|
|
||||||
import org.elasticsearch.ResourceNotFoundException;
|
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse.AliasActionResult;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
|
@ -19,6 +20,7 @@ import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.core.Tuple;
|
import org.elasticsearch.core.Tuple;
|
||||||
import org.elasticsearch.index.IndexNotFoundException;
|
import org.elasticsearch.index.IndexNotFoundException;
|
||||||
import org.elasticsearch.index.IndexVersion;
|
import org.elasticsearch.index.IndexVersion;
|
||||||
|
import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException;
|
||||||
import org.elasticsearch.test.ClusterServiceUtils;
|
import org.elasticsearch.test.ClusterServiceUtils;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.index.IndexVersionUtils;
|
import org.elasticsearch.test.index.IndexVersionUtils;
|
||||||
|
@ -156,11 +158,11 @@ public class MetadataIndexAliasesServiceTests extends ESTestCase {
|
||||||
|
|
||||||
// Show that removing non-existing alias with mustExist == true fails
|
// Show that removing non-existing alias with mustExist == true fails
|
||||||
final ClusterState finalCS = after;
|
final ClusterState finalCS = after;
|
||||||
final ResourceNotFoundException iae = expectThrows(
|
final AliasesNotFoundException iae = expectThrows(
|
||||||
ResourceNotFoundException.class,
|
AliasesNotFoundException.class,
|
||||||
() -> service.applyAliasActions(finalCS, singletonList(new AliasAction.Remove(index, "test_2", true)))
|
() -> service.applyAliasActions(finalCS, singletonList(new AliasAction.Remove(index, "test_2", true)))
|
||||||
);
|
);
|
||||||
assertThat(iae.getMessage(), containsString("required alias [test_2] does not exist"));
|
assertThat(iae.getMessage(), containsString("aliases [test_2] missing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultipleIndices() {
|
public void testMultipleIndices() {
|
||||||
|
@ -690,10 +692,12 @@ public class MetadataIndexAliasesServiceTests extends ESTestCase {
|
||||||
String index = randomAlphaOfLength(5);
|
String index = randomAlphaOfLength(5);
|
||||||
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), index);
|
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), index);
|
||||||
IndicesAliasesClusterStateUpdateRequest addAliasRequest = new IndicesAliasesClusterStateUpdateRequest(
|
IndicesAliasesClusterStateUpdateRequest addAliasRequest = new IndicesAliasesClusterStateUpdateRequest(
|
||||||
List.of(new AliasAction.Add(index, "test", null, null, null, null, null))
|
List.of(new AliasAction.Add(index, "test", null, null, null, null, null)),
|
||||||
|
List.of(AliasActionResult.buildSuccess(List.of(index), AliasActions.add().aliases("test").indices(index)))
|
||||||
);
|
);
|
||||||
IndicesAliasesClusterStateUpdateRequest removeAliasRequest = new IndicesAliasesClusterStateUpdateRequest(
|
IndicesAliasesClusterStateUpdateRequest removeAliasRequest = new IndicesAliasesClusterStateUpdateRequest(
|
||||||
List.of(new AliasAction.Remove(index, "test", true))
|
List.of(new AliasAction.Remove(index, "test", true)),
|
||||||
|
List.of(AliasActionResult.buildSuccess(List.of(index), AliasActions.remove().aliases("test").indices(index)))
|
||||||
);
|
);
|
||||||
|
|
||||||
ClusterState after = ClusterStateTaskExecutorUtils.executeAndAssertSuccessful(
|
ClusterState after = ClusterStateTaskExecutorUtils.executeAndAssertSuccessful(
|
||||||
|
|
|
@ -14,9 +14,9 @@ import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
|
||||||
import org.elasticsearch.action.admin.cluster.health.TransportClusterHealthAction;
|
import org.elasticsearch.action.admin.cluster.health.TransportClusterHealthAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
|
||||||
import org.elasticsearch.client.internal.Client;
|
import org.elasticsearch.client.internal.Client;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.metadata.IndexAbstraction;
|
import org.elasticsearch.cluster.metadata.IndexAbstraction;
|
||||||
|
@ -130,7 +130,9 @@ public class AnnotationIndex {
|
||||||
client.threadPool().getThreadContext(),
|
client.threadPool().getThreadContext(),
|
||||||
ML_ORIGIN,
|
ML_ORIGIN,
|
||||||
requestBuilder.request(),
|
requestBuilder.request(),
|
||||||
finalDelegate.<AcknowledgedResponse>delegateFailureAndWrap((l, r) -> checkMappingsListener.onResponse(r.isAcknowledged())),
|
finalDelegate.<IndicesAliasesResponse>delegateFailureAndWrap(
|
||||||
|
(l, r) -> checkMappingsListener.onResponse(r.isAcknowledged())
|
||||||
|
),
|
||||||
client.admin().indices()::aliases
|
client.admin().indices()::aliases
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
|
@ -295,7 +296,7 @@ public final class MlIndexAndAlias {
|
||||||
client.threadPool().getThreadContext(),
|
client.threadPool().getThreadContext(),
|
||||||
ML_ORIGIN,
|
ML_ORIGIN,
|
||||||
request,
|
request,
|
||||||
listener.<AcknowledgedResponse>delegateFailureAndWrap((l, resp) -> l.onResponse(resp.isAcknowledged())),
|
listener.<IndicesAliasesResponse>delegateFailureAndWrap((l, resp) -> l.onResponse(resp.isAcknowledged())),
|
||||||
client.admin().indices()::aliases
|
client.admin().indices()::aliases
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ package org.elasticsearch.xpack.core.ilm;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
|
||||||
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
||||||
import org.elasticsearch.index.IndexVersion;
|
import org.elasticsearch.index.IndexVersion;
|
||||||
|
@ -90,8 +90,8 @@ public class ShrinkSetAliasStepTests extends AbstractStepTestCase<ShrinkSetAlias
|
||||||
IndicesAliasesRequest request = (IndicesAliasesRequest) invocation.getArguments()[0];
|
IndicesAliasesRequest request = (IndicesAliasesRequest) invocation.getArguments()[0];
|
||||||
assertThat(request.getAliasActions(), equalTo(expectedAliasActions));
|
assertThat(request.getAliasActions(), equalTo(expectedAliasActions));
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ActionListener<AcknowledgedResponse> listener = (ActionListener<AcknowledgedResponse>) invocation.getArguments()[1];
|
ActionListener<IndicesAliasesResponse> listener = (ActionListener<IndicesAliasesResponse>) invocation.getArguments()[1];
|
||||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
listener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||||
return null;
|
return null;
|
||||||
}).when(indicesClient).aliases(Mockito.any(), Mockito.any());
|
}).when(indicesClient).aliases(Mockito.any(), Mockito.any());
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ public class ShrinkSetAliasStepTests extends AbstractStepTestCase<ShrinkSetAlias
|
||||||
|
|
||||||
Mockito.doAnswer((Answer<Void>) invocation -> {
|
Mockito.doAnswer((Answer<Void>) invocation -> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ActionListener<AcknowledgedResponse> listener = (ActionListener<AcknowledgedResponse>) invocation.getArguments()[1];
|
ActionListener<IndicesAliasesResponse> listener = (ActionListener<IndicesAliasesResponse>) invocation.getArguments()[1];
|
||||||
listener.onFailure(exception);
|
listener.onFailure(exception);
|
||||||
return null;
|
return null;
|
||||||
}).when(indicesClient).aliases(Mockito.any(), Mockito.any());
|
}).when(indicesClient).aliases(Mockito.any(), Mockito.any());
|
||||||
|
|
|
@ -13,11 +13,11 @@ import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction;
|
import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
|
||||||
import org.elasticsearch.action.support.master.MasterNodeRequest;
|
import org.elasticsearch.action.support.master.MasterNodeRequest;
|
||||||
import org.elasticsearch.client.internal.AdminClient;
|
import org.elasticsearch.client.internal.AdminClient;
|
||||||
import org.elasticsearch.client.internal.Client;
|
import org.elasticsearch.client.internal.Client;
|
||||||
|
@ -97,8 +97,8 @@ public class MlIndexAndAliasTests extends ESTestCase {
|
||||||
);
|
);
|
||||||
doAnswer(withResponse(new CreateIndexResponse(true, true, FIRST_CONCRETE_INDEX))).when(indicesAdminClient).create(any(), any());
|
doAnswer(withResponse(new CreateIndexResponse(true, true, FIRST_CONCRETE_INDEX))).when(indicesAdminClient).create(any(), any());
|
||||||
when(indicesAdminClient.prepareAliases()).thenReturn(new IndicesAliasesRequestBuilder(client));
|
when(indicesAdminClient.prepareAliases()).thenReturn(new IndicesAliasesRequestBuilder(client));
|
||||||
doAnswer(withResponse(AcknowledgedResponse.TRUE)).when(indicesAdminClient).aliases(any(), any());
|
doAnswer(withResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS)).when(indicesAdminClient).aliases(any(), any());
|
||||||
doAnswer(withResponse(AcknowledgedResponse.TRUE)).when(indicesAdminClient).putTemplate(any(), any());
|
doAnswer(withResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS)).when(indicesAdminClient).putTemplate(any(), any());
|
||||||
|
|
||||||
clusterAdminClient = mock(ClusterAdminClient.class);
|
clusterAdminClient = mock(ClusterAdminClient.class);
|
||||||
doAnswer(invocationOnMock -> {
|
doAnswer(invocationOnMock -> {
|
||||||
|
@ -116,8 +116,9 @@ public class MlIndexAndAliasTests extends ESTestCase {
|
||||||
when(client.threadPool()).thenReturn(threadPool);
|
when(client.threadPool()).thenReturn(threadPool);
|
||||||
when(client.admin()).thenReturn(adminClient);
|
when(client.admin()).thenReturn(adminClient);
|
||||||
doAnswer(invocationOnMock -> {
|
doAnswer(invocationOnMock -> {
|
||||||
ActionListener<AcknowledgedResponse> actionListener = (ActionListener<AcknowledgedResponse>) invocationOnMock.getArguments()[2];
|
ActionListener<IndicesAliasesResponse> actionListener = (ActionListener<IndicesAliasesResponse>) invocationOnMock
|
||||||
actionListener.onResponse(AcknowledgedResponse.TRUE);
|
.getArguments()[2];
|
||||||
|
actionListener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||||
return null;
|
return null;
|
||||||
}).when(client)
|
}).when(client)
|
||||||
.execute(
|
.execute(
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.action.DocWriteRequest;
|
||||||
import org.elasticsearch.action.DocWriteResponse;
|
import org.elasticsearch.action.DocWriteResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||||
import org.elasticsearch.action.delete.DeleteRequest;
|
import org.elasticsearch.action.delete.DeleteRequest;
|
||||||
|
@ -223,7 +224,7 @@ public class SearchApplicationIndexService {
|
||||||
public void putSearchApplication(SearchApplication app, boolean create, ActionListener<DocWriteResponse> listener) {
|
public void putSearchApplication(SearchApplication app, boolean create, ActionListener<DocWriteResponse> listener) {
|
||||||
createOrUpdateAlias(app, new ActionListener<>() {
|
createOrUpdateAlias(app, new ActionListener<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
|
public void onResponse(IndicesAliasesResponse response) {
|
||||||
updateSearchApplication(app, create, listener);
|
updateSearchApplication(app, create, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +241,7 @@ public class SearchApplicationIndexService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createOrUpdateAlias(SearchApplication app, ActionListener<AcknowledgedResponse> listener) {
|
private void createOrUpdateAlias(SearchApplication app, ActionListener<IndicesAliasesResponse> listener) {
|
||||||
|
|
||||||
final Metadata metadata = clusterService.state().metadata();
|
final Metadata metadata = clusterService.state().metadata();
|
||||||
final String searchAliasName = getSearchAliasName(app);
|
final String searchAliasName = getSearchAliasName(app);
|
||||||
|
@ -332,14 +333,14 @@ public class SearchApplicationIndexService {
|
||||||
);
|
);
|
||||||
client.admin().indices().aliases(aliasesRequest, new ActionListener<>() {
|
client.admin().indices().aliases(aliasesRequest, new ActionListener<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
|
public void onResponse(IndicesAliasesResponse response) {
|
||||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
listener.onResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
if (e instanceof ResourceNotFoundException) {
|
if (e instanceof ResourceNotFoundException) {
|
||||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
listener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||||
} else {
|
} else {
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
|
import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||||
|
@ -173,7 +174,7 @@ public final class MlInitializationService implements ClusterStateListener {
|
||||||
String[] mlHiddenIndexPatterns = MachineLearning.getMlHiddenIndexPatterns();
|
String[] mlHiddenIndexPatterns = MachineLearning.getMlHiddenIndexPatterns();
|
||||||
|
|
||||||
// Step 5: Handle errors encountered on the way.
|
// Step 5: Handle errors encountered on the way.
|
||||||
ActionListener<AcknowledgedResponse> finalListener = ActionListener.wrap(updateAliasesResponse -> {
|
ActionListener<IndicesAliasesResponse> finalListener = ActionListener.wrap(updateAliasesResponse -> {
|
||||||
if (updateAliasesResponse.isAcknowledged() == false) {
|
if (updateAliasesResponse.isAcknowledged() == false) {
|
||||||
logger.warn("One or more of the ML internal aliases could not be made hidden.");
|
logger.warn("One or more of the ML internal aliases could not be made hidden.");
|
||||||
return;
|
return;
|
||||||
|
@ -194,7 +195,7 @@ public final class MlInitializationService implements ClusterStateListener {
|
||||||
}
|
}
|
||||||
if (indicesAliasesRequest.getAliasActions().isEmpty()) {
|
if (indicesAliasesRequest.getAliasActions().isEmpty()) {
|
||||||
logger.debug("There are no ML internal aliases that need to be made hidden, [{}]", getAliasesResponse.getAliases());
|
logger.debug("There are no ML internal aliases that need to be made hidden, [{}]", getAliasesResponse.getAliases());
|
||||||
finalListener.onResponse(AcknowledgedResponse.TRUE);
|
finalListener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String indicesWithNonHiddenAliasesString = indicesAliasesRequest.getAliasActions()
|
String indicesWithNonHiddenAliasesString = indicesAliasesRequest.getAliasActions()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||||
|
@ -287,7 +288,7 @@ public class JobDataDeleter {
|
||||||
|
|
||||||
AtomicReference<String[]> indexNames = new AtomicReference<>();
|
AtomicReference<String[]> indexNames = new AtomicReference<>();
|
||||||
|
|
||||||
final ActionListener<AcknowledgedResponse> completionHandler = ActionListener.wrap(
|
final ActionListener<IndicesAliasesResponse> completionHandler = ActionListener.wrap(
|
||||||
response -> finishedHandler.accept(response.isAcknowledged()),
|
response -> finishedHandler.accept(response.isAcknowledged()),
|
||||||
failureHandler
|
failureHandler
|
||||||
);
|
);
|
||||||
|
@ -295,7 +296,7 @@ public class JobDataDeleter {
|
||||||
// Step 9. If we did not drop the indices and after DBQ state done, we delete the aliases
|
// Step 9. If we did not drop the indices and after DBQ state done, we delete the aliases
|
||||||
ActionListener<BulkByScrollResponse> dbqHandler = ActionListener.wrap(bulkByScrollResponse -> {
|
ActionListener<BulkByScrollResponse> dbqHandler = ActionListener.wrap(bulkByScrollResponse -> {
|
||||||
if (bulkByScrollResponse == null) { // no action was taken by DBQ, assume indices were deleted
|
if (bulkByScrollResponse == null) { // no action was taken by DBQ, assume indices were deleted
|
||||||
completionHandler.onResponse(AcknowledgedResponse.TRUE);
|
completionHandler.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||||
} else {
|
} else {
|
||||||
if (bulkByScrollResponse.isTimedOut()) {
|
if (bulkByScrollResponse.isTimedOut()) {
|
||||||
logger.warn("[{}] DeleteByQuery for indices [{}] timed out.", jobId, String.join(", ", indexNames.get()));
|
logger.warn("[{}] DeleteByQuery for indices [{}] timed out.", jobId, String.join(", ", indexNames.get()));
|
||||||
|
@ -469,7 +470,7 @@ public class JobDataDeleter {
|
||||||
executeAsyncWithOrigin(client, ML_ORIGIN, RefreshAction.INSTANCE, refreshRequest, refreshListener);
|
executeAsyncWithOrigin(client, ML_ORIGIN, RefreshAction.INSTANCE, refreshRequest, refreshListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteAliases(@SuppressWarnings("HiddenField") String jobId, ActionListener<AcknowledgedResponse> finishedHandler) {
|
private void deleteAliases(@SuppressWarnings("HiddenField") String jobId, ActionListener<IndicesAliasesResponse> finishedHandler) {
|
||||||
final String readAliasName = AnomalyDetectorsIndex.jobResultsAliasedName(jobId);
|
final String readAliasName = AnomalyDetectorsIndex.jobResultsAliasedName(jobId);
|
||||||
final String writeAliasName = AnomalyDetectorsIndex.resultsWriteAlias(jobId);
|
final String writeAliasName = AnomalyDetectorsIndex.resultsWriteAlias(jobId);
|
||||||
|
|
||||||
|
@ -486,7 +487,7 @@ public class JobDataDeleter {
|
||||||
if (removeRequest == null) {
|
if (removeRequest == null) {
|
||||||
// don't error if the job's aliases have already been deleted - carry on and delete the
|
// don't error if the job's aliases have already been deleted - carry on and delete the
|
||||||
// rest of the job's data
|
// rest of the job's data
|
||||||
finishedHandler.onResponse(AcknowledgedResponse.TRUE);
|
finishedHandler.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
executeAsyncWithOrigin(
|
executeAsyncWithOrigin(
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.ResourceNotFoundException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.DelegatingActionListener;
|
import org.elasticsearch.action.DelegatingActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||||
|
@ -344,7 +345,7 @@ public class JobResultsProvider {
|
||||||
client.threadPool().getThreadContext(),
|
client.threadPool().getThreadContext(),
|
||||||
ML_ORIGIN,
|
ML_ORIGIN,
|
||||||
request,
|
request,
|
||||||
ActionListener.<AcknowledgedResponse>wrap(r -> finalListener.onResponse(true), finalListener::onFailure),
|
ActionListener.<IndicesAliasesResponse>wrap(r -> finalListener.onResponse(true), finalListener::onFailure),
|
||||||
client.admin().indices()::aliases
|
client.admin().indices()::aliases
|
||||||
);
|
);
|
||||||
}, finalListener::onFailure);
|
}, finalListener::onFailure);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||||
import org.elasticsearch.client.internal.Client;
|
import org.elasticsearch.client.internal.Client;
|
||||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
|
@ -97,7 +97,7 @@ class TransformClusterStateListener implements ClusterStateListener {
|
||||||
client.threadPool().getThreadContext(),
|
client.threadPool().getThreadContext(),
|
||||||
TRANSFORM_ORIGIN,
|
TRANSFORM_ORIGIN,
|
||||||
request,
|
request,
|
||||||
ActionListener.<AcknowledgedResponse>wrap(r -> finalListener.onResponse(r.isAcknowledged()), finalListener::onFailure),
|
ActionListener.<IndicesAliasesResponse>wrap(r -> finalListener.onResponse(r.isAcknowledged()), finalListener::onFailure),
|
||||||
client.admin().indices()::aliases
|
client.admin().indices()::aliases
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue