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/]
|
||||
// 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]
|
||||
[[add-alias-at-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.
|
||||
|
||||
// tag::alias-options[]
|
||||
`must_exist`::
|
||||
(Optional, Boolean)
|
||||
If `true`, the alias must exist to perform the action. Defaults to `false`. Only
|
||||
the `remove` action supports this parameter.
|
||||
Affects the behavior when attempting to remove an alias which does not exist.
|
||||
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[]
|
||||
`routing`::
|
||||
|
@ -168,3 +174,51 @@ stream aliases don't support 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:
|
||||
name: this-does-not-exist*
|
||||
- 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:
|
||||
index: test_index
|
||||
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 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 ALIAS_ACTION_RESULTS = def(8_626_00_0);
|
||||
|
||||
/*
|
||||
* STOP! READ THIS FIRST! No, really,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
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.metadata.AliasAction;
|
||||
|
||||
|
@ -18,8 +19,11 @@ import java.util.List;
|
|||
public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest<IndicesAliasesClusterStateUpdateRequest> {
|
||||
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.actionResults = actionResults;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,4 +32,8 @@ public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateR
|
|||
public List<AliasAction> 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_HIDDEN = new ParseField("is_hidden");
|
||||
private static final ParseField MUST_EXIST = new ParseField("must_exist");
|
||||
|
||||
private static final ParseField ADD = new ParseField("add");
|
||||
private static final ParseField REMOVE = new ParseField("remove");
|
||||
private static final ParseField REMOVE_INDEX = new ParseField("remove_index");
|
||||
|
@ -105,6 +104,10 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
return value;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public static Type fromValue(byte value) {
|
||||
return switch (value) {
|
||||
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.support.master.AcknowledgedRequestBuilder;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.client.internal.ElasticsearchClient;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
|
@ -21,7 +20,7 @@ import java.util.Map;
|
|||
*/
|
||||
public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<
|
||||
IndicesAliasesRequest,
|
||||
AcknowledgedResponse,
|
||||
IndicesAliasesResponse,
|
||||
IndicesAliasesRequestBuilder> {
|
||||
|
||||
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.RequestValidators;
|
||||
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.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedTransportMasterNodeAction;
|
||||
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
|
@ -56,10 +56,10 @@ import static java.util.Collections.unmodifiableList;
|
|||
/**
|
||||
* 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 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 final MetadataIndexAliasesService indexAliasesService;
|
||||
|
@ -85,6 +85,7 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
|||
actionFilters,
|
||||
IndicesAliasesRequest::new,
|
||||
indexNameExpressionResolver,
|
||||
IndicesAliasesResponse::new,
|
||||
EsExecutors.DIRECT_EXECUTOR_SERVICE
|
||||
);
|
||||
this.indexAliasesService = indexAliasesService;
|
||||
|
@ -106,15 +107,19 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
|||
Task task,
|
||||
final IndicesAliasesRequest request,
|
||||
final ClusterState state,
|
||||
final ActionListener<AcknowledgedResponse> listener
|
||||
final ActionListener<IndicesAliasesResponse> listener
|
||||
) {
|
||||
|
||||
// Expand the indices names
|
||||
List<AliasActions> actions = request.aliasActions();
|
||||
List<AliasAction> finalActions = new ArrayList<>();
|
||||
List<AliasActionResult> actionResults = new ArrayList<>();
|
||||
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
||||
Set<String> aliases = new HashSet<>();
|
||||
for (AliasActions action : actions) {
|
||||
int numAliasesRemoved = 0;
|
||||
List<String> resolvedIndices = new ArrayList<>();
|
||||
|
||||
List<String> concreteDataStreams = indexNameExpressionResolver.dataStreamNames(
|
||||
state,
|
||||
request.indicesOptions(),
|
||||
|
@ -161,18 +166,24 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
|||
finalActions.add(new AddDataStreamAlias(alias, dataStreamName, action.writeIndex(), action.filter()));
|
||||
}
|
||||
}
|
||||
|
||||
actionResults.add(AliasActionResult.buildSuccess(concreteDataStreams, action));
|
||||
continue;
|
||||
}
|
||||
case REMOVE -> {
|
||||
for (String dataStreamName : concreteDataStreams) {
|
||||
for (String alias : concreteDataStreamAliases(action, state.metadata(), dataStreamName)) {
|
||||
finalActions.add(new AliasAction.RemoveDataStreamAlias(alias, dataStreamName, action.mustExist()));
|
||||
numAliasesRemoved++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nonBackingIndices.isEmpty() == false) {
|
||||
// Regular aliases/indices match as well with the provided expression.
|
||||
// (Only when adding new aliases, matching both data streams and indices is disallowed)
|
||||
resolvedIndices.addAll(concreteDataStreams);
|
||||
} else {
|
||||
actionResults.add(AliasActionResult.build(concreteDataStreams, action, numAliasesRemoved));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +235,7 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
|||
case REMOVE:
|
||||
for (String alias : concreteAliases(action, state.metadata(), index.getName())) {
|
||||
finalActions.add(new AliasAction.Remove(index.getName(), alias, action.mustExist()));
|
||||
numAliasesRemoved++;
|
||||
}
|
||||
break;
|
||||
case REMOVE_INDEX:
|
||||
|
@ -233,14 +245,18 @@ public class TransportIndicesAliasesAction extends AcknowledgedTransportMasterNo
|
|||
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()) {
|
||||
throw new AliasesNotFoundException(aliases.toArray(new String[aliases.size()]));
|
||||
}
|
||||
request.aliasActions().clear();
|
||||
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(unmodifiableList(finalActions))
|
||||
.ackTimeout(request.timeout())
|
||||
.masterNodeTimeout(request.masterNodeTimeout());
|
||||
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(
|
||||
unmodifiableList(finalActions),
|
||||
unmodifiableList(actionResults)
|
||||
).ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout());
|
||||
|
||||
indexAliasesService.indicesAliases(updateRequest, listener.delegateResponse((l, 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.admin.indices.alias.IndicesAliasesRequest;
|
||||
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.GetAliasesRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||
|
@ -371,7 +372,7 @@ public interface IndicesAdminClient extends ElasticsearchClient {
|
|||
* @param request The index aliases request
|
||||
* @return The result future
|
||||
*/
|
||||
ActionFuture<AcknowledgedResponse> aliases(IndicesAliasesRequest request);
|
||||
ActionFuture<IndicesAliasesResponse> aliases(IndicesAliasesRequest request);
|
||||
|
||||
/**
|
||||
* Allows to add/remove aliases from indices.
|
||||
|
@ -379,7 +380,7 @@ public interface IndicesAdminClient extends ElasticsearchClient {
|
|||
* @param request The index aliases request
|
||||
* @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.
|
||||
|
|
|
@ -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.indices.alias.IndicesAliasesRequest;
|
||||
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.get.GetAliasesAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
|
@ -1083,12 +1084,12 @@ public abstract class AbstractClient implements Client {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<AcknowledgedResponse> aliases(final IndicesAliasesRequest request) {
|
||||
public ActionFuture<IndicesAliasesResponse> aliases(final IndicesAliasesRequest request) {
|
||||
return execute(TransportIndicesAliasesAction.TYPE, request);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.ResourceNotFoundException;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.common.Strings;
|
||||
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}.
|
||||
|
@ -189,7 +189,7 @@ public abstract class AliasAction {
|
|||
boolean apply(NewAliasValidator aliasValidator, Metadata.Builder metadata, IndexMetadata index) {
|
||||
if (false == index.getAliases().containsKey(alias)) {
|
||||
if (mustExist != null && mustExist) {
|
||||
throw new ResourceNotFoundException("required alias [" + alias + "] does not exist");
|
||||
throw new AliasesNotFoundException(alias);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ package org.elasticsearch.cluster.metadata;
|
|||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
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.ClusterStateAckListener;
|
||||
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
||||
|
@ -79,7 +79,10 @@ public class MetadataIndexAliasesService {
|
|||
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?
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
record ApplyAliasesTask(IndicesAliasesClusterStateUpdateRequest request, ActionListener<AcknowledgedResponse> listener)
|
||||
record ApplyAliasesTask(IndicesAliasesClusterStateUpdateRequest request, ActionListener<IndicesAliasesResponse> listener)
|
||||
implements
|
||||
ClusterStateTaskListener,
|
||||
ClusterStateAckListener {
|
||||
|
@ -271,17 +274,17 @@ public class MetadataIndexAliasesService {
|
|||
|
||||
@Override
|
||||
public void onAllNodesAcked() {
|
||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
||||
listener.onResponse(IndicesAliasesResponse.build(request.getActionResults()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAckFailure(Exception e) {
|
||||
listener.onResponse(AcknowledgedResponse.FALSE);
|
||||
listener.onResponse(IndicesAliasesResponse.NOT_ACKNOWLEDGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAckTimeout() {
|
||||
listener.onResponse(AcknowledgedResponse.FALSE);
|
||||
listener.onResponse(IndicesAliasesResponse.NOT_ACKNOWLEDGED);
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
import org.elasticsearch.ResourceNotFoundException;
|
||||
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.ClusterState;
|
||||
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.index.IndexNotFoundException;
|
||||
import org.elasticsearch.index.IndexVersion;
|
||||
import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException;
|
||||
import org.elasticsearch.test.ClusterServiceUtils;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
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
|
||||
final ClusterState finalCS = after;
|
||||
final ResourceNotFoundException iae = expectThrows(
|
||||
ResourceNotFoundException.class,
|
||||
final AliasesNotFoundException iae = expectThrows(
|
||||
AliasesNotFoundException.class,
|
||||
() -> 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() {
|
||||
|
@ -690,10 +692,12 @@ public class MetadataIndexAliasesServiceTests extends ESTestCase {
|
|||
String index = randomAlphaOfLength(5);
|
||||
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), index);
|
||||
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(
|
||||
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(
|
||||
|
|
|
@ -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.indices.alias.IndicesAliasesRequest;
|
||||
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.CreateIndexResponse;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.client.internal.Client;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexAbstraction;
|
||||
|
@ -130,7 +130,9 @@ public class AnnotationIndex {
|
|||
client.threadPool().getThreadContext(),
|
||||
ML_ORIGIN,
|
||||
requestBuilder.request(),
|
||||
finalDelegate.<AcknowledgedResponse>delegateFailureAndWrap((l, r) -> checkMappingsListener.onResponse(r.isAcknowledged())),
|
||||
finalDelegate.<IndicesAliasesResponse>delegateFailureAndWrap(
|
||||
(l, r) -> checkMappingsListener.onResponse(r.isAcknowledged())
|
||||
),
|
||||
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.IndicesAliasesRequest;
|
||||
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.CreateIndexRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||
|
@ -295,7 +296,7 @@ public final class MlIndexAndAlias {
|
|||
client.threadPool().getThreadContext(),
|
||||
ML_ORIGIN,
|
||||
request,
|
||||
listener.<AcknowledgedResponse>delegateFailureAndWrap((l, resp) -> l.onResponse(resp.isAcknowledged())),
|
||||
listener.<IndicesAliasesResponse>delegateFailureAndWrap((l, resp) -> l.onResponse(resp.isAcknowledged())),
|
||||
client.admin().indices()::aliases
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ package org.elasticsearch.xpack.core.ilm;
|
|||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
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.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
||||
import org.elasticsearch.index.IndexVersion;
|
||||
|
@ -90,8 +90,8 @@ public class ShrinkSetAliasStepTests extends AbstractStepTestCase<ShrinkSetAlias
|
|||
IndicesAliasesRequest request = (IndicesAliasesRequest) invocation.getArguments()[0];
|
||||
assertThat(request.getAliasActions(), equalTo(expectedAliasActions));
|
||||
@SuppressWarnings("unchecked")
|
||||
ActionListener<AcknowledgedResponse> listener = (ActionListener<AcknowledgedResponse>) invocation.getArguments()[1];
|
||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
||||
ActionListener<IndicesAliasesResponse> listener = (ActionListener<IndicesAliasesResponse>) invocation.getArguments()[1];
|
||||
listener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||
return null;
|
||||
}).when(indicesClient).aliases(Mockito.any(), Mockito.any());
|
||||
|
||||
|
@ -113,7 +113,7 @@ public class ShrinkSetAliasStepTests extends AbstractStepTestCase<ShrinkSetAlias
|
|||
|
||||
Mockito.doAnswer((Answer<Void>) invocation -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
ActionListener<AcknowledgedResponse> listener = (ActionListener<AcknowledgedResponse>) invocation.getArguments()[1];
|
||||
ActionListener<IndicesAliasesResponse> listener = (ActionListener<IndicesAliasesResponse>) invocation.getArguments()[1];
|
||||
listener.onFailure(exception);
|
||||
return null;
|
||||
}).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.AliasActions;
|
||||
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.CreateIndexRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||
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.client.internal.AdminClient;
|
||||
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());
|
||||
when(indicesAdminClient.prepareAliases()).thenReturn(new IndicesAliasesRequestBuilder(client));
|
||||
doAnswer(withResponse(AcknowledgedResponse.TRUE)).when(indicesAdminClient).aliases(any(), any());
|
||||
doAnswer(withResponse(AcknowledgedResponse.TRUE)).when(indicesAdminClient).putTemplate(any(), any());
|
||||
doAnswer(withResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS)).when(indicesAdminClient).aliases(any(), any());
|
||||
doAnswer(withResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS)).when(indicesAdminClient).putTemplate(any(), any());
|
||||
|
||||
clusterAdminClient = mock(ClusterAdminClient.class);
|
||||
doAnswer(invocationOnMock -> {
|
||||
|
@ -116,8 +116,9 @@ public class MlIndexAndAliasTests extends ESTestCase {
|
|||
when(client.threadPool()).thenReturn(threadPool);
|
||||
when(client.admin()).thenReturn(adminClient);
|
||||
doAnswer(invocationOnMock -> {
|
||||
ActionListener<AcknowledgedResponse> actionListener = (ActionListener<AcknowledgedResponse>) invocationOnMock.getArguments()[2];
|
||||
actionListener.onResponse(AcknowledgedResponse.TRUE);
|
||||
ActionListener<IndicesAliasesResponse> actionListener = (ActionListener<IndicesAliasesResponse>) invocationOnMock
|
||||
.getArguments()[2];
|
||||
actionListener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||
return null;
|
||||
}).when(client)
|
||||
.execute(
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.action.DocWriteRequest;
|
|||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
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.GetAliasesResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
|
@ -223,7 +224,7 @@ public class SearchApplicationIndexService {
|
|||
public void putSearchApplication(SearchApplication app, boolean create, ActionListener<DocWriteResponse> listener) {
|
||||
createOrUpdateAlias(app, new ActionListener<>() {
|
||||
@Override
|
||||
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
|
||||
public void onResponse(IndicesAliasesResponse response) {
|
||||
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 String searchAliasName = getSearchAliasName(app);
|
||||
|
@ -332,14 +333,14 @@ public class SearchApplicationIndexService {
|
|||
);
|
||||
client.admin().indices().aliases(aliasesRequest, new ActionListener<>() {
|
||||
@Override
|
||||
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
|
||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
||||
public void onResponse(IndicesAliasesResponse response) {
|
||||
listener.onResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
if (e instanceof ResourceNotFoundException) {
|
||||
listener.onResponse(AcknowledgedResponse.TRUE);
|
||||
listener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||
} else {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
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.get.GetAliasesAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
|
@ -173,7 +174,7 @@ public final class MlInitializationService implements ClusterStateListener {
|
|||
String[] mlHiddenIndexPatterns = MachineLearning.getMlHiddenIndexPatterns();
|
||||
|
||||
// Step 5: Handle errors encountered on the way.
|
||||
ActionListener<AcknowledgedResponse> finalListener = ActionListener.wrap(updateAliasesResponse -> {
|
||||
ActionListener<IndicesAliasesResponse> finalListener = ActionListener.wrap(updateAliasesResponse -> {
|
||||
if (updateAliasesResponse.isAcknowledged() == false) {
|
||||
logger.warn("One or more of the ML internal aliases could not be made hidden.");
|
||||
return;
|
||||
|
@ -194,7 +195,7 @@ public final class MlInitializationService implements ClusterStateListener {
|
|||
}
|
||||
if (indicesAliasesRequest.getAliasActions().isEmpty()) {
|
||||
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;
|
||||
}
|
||||
String indicesWithNonHiddenAliasesString = indicesAliasesRequest.getAliasActions()
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
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.GetAliasesResponse;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
|
@ -287,7 +288,7 @@ public class JobDataDeleter {
|
|||
|
||||
AtomicReference<String[]> indexNames = new AtomicReference<>();
|
||||
|
||||
final ActionListener<AcknowledgedResponse> completionHandler = ActionListener.wrap(
|
||||
final ActionListener<IndicesAliasesResponse> completionHandler = ActionListener.wrap(
|
||||
response -> finishedHandler.accept(response.isAcknowledged()),
|
||||
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
|
||||
ActionListener<BulkByScrollResponse> dbqHandler = ActionListener.wrap(bulkByScrollResponse -> {
|
||||
if (bulkByScrollResponse == null) { // no action was taken by DBQ, assume indices were deleted
|
||||
completionHandler.onResponse(AcknowledgedResponse.TRUE);
|
||||
completionHandler.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||
} else {
|
||||
if (bulkByScrollResponse.isTimedOut()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 writeAliasName = AnomalyDetectorsIndex.resultsWriteAlias(jobId);
|
||||
|
||||
|
@ -486,7 +487,7 @@ public class JobDataDeleter {
|
|||
if (removeRequest == null) {
|
||||
// don't error if the job's aliases have already been deleted - carry on and delete the
|
||||
// rest of the job's data
|
||||
finishedHandler.onResponse(AcknowledgedResponse.TRUE);
|
||||
finishedHandler.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
|
||||
return;
|
||||
}
|
||||
executeAsyncWithOrigin(
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.ResourceNotFoundException;
|
|||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.DelegatingActionListener;
|
||||
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.CreateIndexResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
|
@ -344,7 +345,7 @@ public class JobResultsProvider {
|
|||
client.threadPool().getThreadContext(),
|
||||
ML_ORIGIN,
|
||||
request,
|
||||
ActionListener.<AcknowledgedResponse>wrap(r -> finalListener.onResponse(true), finalListener::onFailure),
|
||||
ActionListener.<IndicesAliasesResponse>wrap(r -> finalListener.onResponse(true), finalListener::onFailure),
|
||||
client.admin().indices()::aliases
|
||||
);
|
||||
}, finalListener::onFailure);
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
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.cluster.ClusterChangedEvent;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
|
@ -97,7 +97,7 @@ class TransformClusterStateListener implements ClusterStateListener {
|
|||
client.threadPool().getThreadContext(),
|
||||
TRANSFORM_ORIGIN,
|
||||
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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue