Skip to content

Commit 4b08d38

Browse files
authored
Add APIs (GET/PUT) to decommission awareness attribute (#4261)
* Add APIs (GET/PUT) to decommission awareness attribute Signed-off-by: Rishab Nahata <rnnahata@amazon.com>
1 parent ff2d5be commit 4b08d38

31 files changed

+1093
-20
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
5555
- Further simplification of the ZIP publication implementation ([#4360](https://github.com/opensearch-project/OpenSearch/pull/4360))
5656
- Relax visibility of the HTTP_CHANNEL_KEY and HTTP_SERVER_CHANNEL_KEY to make it possible for the plugins to access associated Netty4HttpChannel / Netty4HttpServerChannel instance ([#4638](https://github.com/opensearch-project/OpenSearch/pull/4638))
5757
- Load the deprecated master role in a dedicated method instead of in setAdditionalRoles() ([#4582](https://github.com/opensearch-project/OpenSearch/pull/4582))
58+
- Add APIs (GET/PUT) to decommission awareness attribute ([#4261](https://github.com/opensearch-project/OpenSearch/pull/4261))
5859

5960
### Deprecated
6061

client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,9 @@ public void testApiNamingConventions() throws Exception {
889889
"nodes.reload_secure_settings",
890890
"search_shards",
891891
"remote_store.restore",
892-
"cluster.put_weighted_routing", };
892+
"cluster.put_weighted_routing",
893+
"cluster.put_decommission_awareness",
894+
"cluster.get_decommission_awareness", };
893895
List<String> booleanReturnMethods = Arrays.asList("security.enable_user", "security.disable_user", "security.change_password");
894896
Set<String> deprecatedMethods = new HashSet<>();
895897
deprecatedMethods.add("indices.force_merge");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"cluster.get_decommission_awareness": {
3+
"documentation": {
4+
"url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/",
5+
"description": "Get details and status of decommissioned attribute"
6+
},
7+
"stability": "experimental",
8+
"url": {
9+
"paths": [
10+
{
11+
"path": "/_cluster/decommission/awareness/_status",
12+
"methods": [
13+
"GET"
14+
]
15+
}
16+
]
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"cluster.put_decommission_awareness": {
3+
"documentation": {
4+
"url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/",
5+
"description": "Decommissions an awareness attribute"
6+
},
7+
"stability": "experimental",
8+
"url": {
9+
"paths": [
10+
{
11+
"path": "/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}",
12+
"methods": [
13+
"PUT"
14+
],
15+
"parts": {
16+
"awareness_attribute_name": {
17+
"type": "string",
18+
"description": "Awareness attribute name"
19+
},
20+
"awareness_attribute_value": {
21+
"type": "string",
22+
"description": "Awareness attribute value"
23+
}
24+
}
25+
}
26+
]
27+
}
28+
}
29+
}

server/src/main/java/org/opensearch/action/ActionModule.java

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction;
4141
import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction;
4242
import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction;
43+
import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateAction;
44+
import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionStateAction;
45+
import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction;
46+
import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportDecommissionAction;
4347
import org.opensearch.action.admin.cluster.health.ClusterHealthAction;
4448
import org.opensearch.action.admin.cluster.health.TransportClusterHealthAction;
4549
import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction;
@@ -306,6 +310,7 @@
306310
import org.opensearch.rest.action.admin.cluster.RestDeleteRepositoryAction;
307311
import org.opensearch.rest.action.admin.cluster.RestDeleteSnapshotAction;
308312
import org.opensearch.rest.action.admin.cluster.RestDeleteStoredScriptAction;
313+
import org.opensearch.rest.action.admin.cluster.RestGetDecommissionStateAction;
309314
import org.opensearch.rest.action.admin.cluster.RestGetRepositoriesAction;
310315
import org.opensearch.rest.action.admin.cluster.RestGetScriptContextAction;
311316
import org.opensearch.rest.action.admin.cluster.RestGetScriptLanguageAction;
@@ -318,6 +323,7 @@
318323
import org.opensearch.rest.action.admin.cluster.RestNodesStatsAction;
319324
import org.opensearch.rest.action.admin.cluster.RestNodesUsageAction;
320325
import org.opensearch.rest.action.admin.cluster.RestPendingClusterTasksAction;
326+
import org.opensearch.rest.action.admin.cluster.RestDecommissionAction;
321327
import org.opensearch.rest.action.admin.cluster.RestPutRepositoryAction;
322328
import org.opensearch.rest.action.admin.cluster.RestPutStoredScriptAction;
323329
import org.opensearch.rest.action.admin.cluster.RestReloadSecureSettingsAction;
@@ -686,6 +692,10 @@ public <Request extends ActionRequest, Response extends ActionResponse> void reg
686692
// Remote Store
687693
actions.register(RestoreRemoteStoreAction.INSTANCE, TransportRestoreRemoteStoreAction.class);
688694

695+
// Decommission actions
696+
actions.register(DecommissionAction.INSTANCE, TransportDecommissionAction.class);
697+
actions.register(GetDecommissionStateAction.INSTANCE, TransportGetDecommissionStateAction.class);
698+
689699
return unmodifiableMap(actions.getRegistry());
690700
}
691701

@@ -879,6 +889,8 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster) {
879889
}
880890
}
881891
registerHandler.accept(new RestCatAction(catActions));
892+
registerHandler.accept(new RestDecommissionAction());
893+
registerHandler.accept(new RestGetDecommissionStateAction());
882894

883895
// Remote Store APIs
884896
if (FeatureFlags.isEnabled(FeatureFlags.REMOTE_STORE)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.action.admin.cluster.decommission.awareness.get;
10+
11+
import org.opensearch.action.ActionType;
12+
13+
/**
14+
* Get decommission action
15+
*
16+
* @opensearch.internal
17+
*/
18+
public class GetDecommissionStateAction extends ActionType<GetDecommissionStateResponse> {
19+
20+
public static final GetDecommissionStateAction INSTANCE = new GetDecommissionStateAction();
21+
public static final String NAME = "cluster:admin/decommission/awareness/get";
22+
23+
private GetDecommissionStateAction() {
24+
super(NAME, GetDecommissionStateResponse::new);
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.action.admin.cluster.decommission.awareness.get;
10+
11+
import org.opensearch.action.ActionRequestValidationException;
12+
import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest;
13+
import org.opensearch.common.io.stream.StreamInput;
14+
import org.opensearch.common.io.stream.StreamOutput;
15+
16+
import java.io.IOException;
17+
18+
/**
19+
* Get Decommissioned attribute request
20+
*
21+
* @opensearch.internal
22+
*/
23+
public class GetDecommissionStateRequest extends ClusterManagerNodeReadRequest<GetDecommissionStateRequest> {
24+
25+
public GetDecommissionStateRequest() {}
26+
27+
public GetDecommissionStateRequest(StreamInput in) throws IOException {
28+
super(in);
29+
}
30+
31+
@Override
32+
public void writeTo(StreamOutput out) throws IOException {
33+
super.writeTo(out);
34+
}
35+
36+
@Override
37+
public ActionRequestValidationException validate() {
38+
return null;
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.action.admin.cluster.decommission.awareness.get;
10+
11+
import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder;
12+
import org.opensearch.client.OpenSearchClient;
13+
14+
/**
15+
* Get decommission request builder
16+
*
17+
* @opensearch.internal
18+
*/
19+
public class GetDecommissionStateRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder<
20+
GetDecommissionStateRequest,
21+
GetDecommissionStateResponse,
22+
GetDecommissionStateRequestBuilder> {
23+
24+
/**
25+
* Creates new get decommissioned attributes request builder
26+
*/
27+
public GetDecommissionStateRequestBuilder(OpenSearchClient client, GetDecommissionStateAction action) {
28+
super(client, action, new GetDecommissionStateRequest());
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.action.admin.cluster.decommission.awareness.get;
10+
11+
import org.opensearch.OpenSearchParseException;
12+
import org.opensearch.action.ActionResponse;
13+
import org.opensearch.cluster.decommission.DecommissionAttribute;
14+
import org.opensearch.cluster.decommission.DecommissionStatus;
15+
import org.opensearch.common.io.stream.StreamInput;
16+
import org.opensearch.common.io.stream.StreamOutput;
17+
import org.opensearch.common.xcontent.ToXContentObject;
18+
import org.opensearch.common.xcontent.XContentBuilder;
19+
import org.opensearch.common.xcontent.XContentParser;
20+
21+
import java.io.IOException;
22+
import java.util.Locale;
23+
import java.util.Objects;
24+
25+
import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
26+
27+
/**
28+
* Response for decommission status
29+
*
30+
* @opensearch.internal
31+
*/
32+
public class GetDecommissionStateResponse extends ActionResponse implements ToXContentObject {
33+
34+
private DecommissionAttribute decommissionedAttribute;
35+
private DecommissionStatus status;
36+
37+
GetDecommissionStateResponse() {
38+
this(null, null);
39+
}
40+
41+
GetDecommissionStateResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) {
42+
this.decommissionedAttribute = decommissionedAttribute;
43+
this.status = status;
44+
}
45+
46+
GetDecommissionStateResponse(StreamInput in) throws IOException {
47+
// read decommissioned attribute and status only if it is present
48+
if (in.readBoolean()) {
49+
this.decommissionedAttribute = new DecommissionAttribute(in);
50+
}
51+
if (in.readBoolean()) {
52+
this.status = DecommissionStatus.fromString(in.readString());
53+
}
54+
}
55+
56+
@Override
57+
public void writeTo(StreamOutput out) throws IOException {
58+
// if decommissioned attribute is null, mark absence of decommissioned attribute
59+
if (decommissionedAttribute == null) {
60+
out.writeBoolean(false);
61+
} else {
62+
out.writeBoolean(true);
63+
decommissionedAttribute.writeTo(out);
64+
}
65+
66+
// if status is null, mark absence of status
67+
if (status == null) {
68+
out.writeBoolean(false);
69+
} else {
70+
out.writeBoolean(true);
71+
out.writeString(status.status());
72+
}
73+
}
74+
75+
public DecommissionAttribute getDecommissionedAttribute() {
76+
return decommissionedAttribute;
77+
}
78+
79+
public DecommissionStatus getDecommissionStatus() {
80+
return status;
81+
}
82+
83+
@Override
84+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
85+
builder.startObject();
86+
builder.startObject("awareness");
87+
if (decommissionedAttribute != null) {
88+
builder.field(decommissionedAttribute.attributeName(), decommissionedAttribute.attributeValue());
89+
}
90+
builder.endObject();
91+
if (status != null) {
92+
builder.field("status", status);
93+
}
94+
builder.endObject();
95+
return builder;
96+
}
97+
98+
public static GetDecommissionStateResponse fromXContent(XContentParser parser) throws IOException {
99+
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
100+
String attributeType = "awareness";
101+
XContentParser.Token token;
102+
DecommissionAttribute decommissionAttribute = null;
103+
DecommissionStatus status = null;
104+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
105+
if (token == XContentParser.Token.FIELD_NAME) {
106+
String currentFieldName = parser.currentName();
107+
if (attributeType.equals(currentFieldName)) {
108+
if (parser.nextToken() != XContentParser.Token.START_OBJECT) {
109+
throw new OpenSearchParseException(
110+
"failed to parse decommission attribute type [{}], expected object",
111+
attributeType
112+
);
113+
}
114+
token = parser.nextToken();
115+
if (token != XContentParser.Token.END_OBJECT) {
116+
if (token == XContentParser.Token.FIELD_NAME) {
117+
String fieldName = parser.currentName();
118+
String value;
119+
token = parser.nextToken();
120+
if (token == XContentParser.Token.VALUE_STRING) {
121+
value = parser.text();
122+
} else {
123+
throw new OpenSearchParseException(
124+
"failed to parse attribute [{}], expected string for attribute value",
125+
fieldName
126+
);
127+
}
128+
decommissionAttribute = new DecommissionAttribute(fieldName, value);
129+
parser.nextToken();
130+
} else {
131+
throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType);
132+
}
133+
} else {
134+
throw new OpenSearchParseException("failed to parse attribute type [{}]", attributeType);
135+
}
136+
} else if ("status".equals(currentFieldName)) {
137+
if (parser.nextToken() != XContentParser.Token.VALUE_STRING) {
138+
throw new OpenSearchParseException(
139+
"failed to parse status of decommissioning, expected string but found unknown type"
140+
);
141+
}
142+
status = DecommissionStatus.fromString(parser.text().toLowerCase(Locale.ROOT));
143+
} else {
144+
throw new OpenSearchParseException(
145+
"unknown field found [{}], failed to parse the decommission attribute",
146+
currentFieldName
147+
);
148+
}
149+
}
150+
}
151+
return new GetDecommissionStateResponse(decommissionAttribute, status);
152+
}
153+
154+
@Override
155+
public boolean equals(Object o) {
156+
if (this == o) return true;
157+
if (o == null || getClass() != o.getClass()) return false;
158+
GetDecommissionStateResponse that = (GetDecommissionStateResponse) o;
159+
return decommissionedAttribute.equals(that.decommissionedAttribute) && status == that.status;
160+
}
161+
162+
@Override
163+
public int hashCode() {
164+
return Objects.hash(decommissionedAttribute, status);
165+
}
166+
}

0 commit comments

Comments
 (0)