Skip to content

Commit c617696

Browse files
authored
[Backport 2.x] Support extension additional settings with extension REST initialization (#8445)
* Support extension additional settings with extension REST initialization (#8414) * Support extension additional settings with extension REST initialization Signed-off-by: Craig Perkins <[email protected]> * Add CHANGELOG entry Signed-off-by: Craig Perkins <[email protected]> * Add tests Signed-off-by: Craig Perkins <[email protected]> * Add addition setting types to test Signed-off-by: Craig Perkins <[email protected]> * Address code review feedback Signed-off-by: Craig Perkins <[email protected]> * Check for missing values Signed-off-by: Craig Perkins <[email protected]> * Use Version.CURRENT Signed-off-by: Craig Perkins <[email protected]> * Switch minimum compat version back to 3.0.0 Signed-off-by: Craig Perkins <[email protected]> * Remove hardcoded versions Signed-off-by: Craig Perkins <[email protected]> --------- Signed-off-by: Craig Perkins <[email protected]> (cherry picked from commit a0299fc) * Update Strings import Signed-off-by: Craig Perkins <[email protected]> --------- Signed-off-by: Craig Perkins <[email protected]>
1 parent 9b9718f commit c617696

File tree

6 files changed

+202
-84
lines changed

6 files changed

+202
-84
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
2121
- Enable Point based optimization for custom comparators ([#8168](https://github.com/opensearch-project/OpenSearch/pull/8168))
2222
- Add GeoTile and GeoHash Grid aggregations on GeoShapes. ([#5589](https://github.com/opensearch-project/OpenSearch/pull/5589))
2323
- Add distributed tracing framework ([#7543](https://github.com/opensearch-project/OpenSearch/issues/7543))
24+
- [Extensions] Support extension additional settings with extension REST initialization ([#8414](https://github.com/opensearch-project/OpenSearch/pull/8414))
2425

2526
### Dependencies
2627
- Bump `com.azure:azure-storage-common` from 12.21.0 to 12.21.1 (#7566, #7814)

Diff for: server/src/main/java/org/opensearch/extensions/ExtensionDependency.java

-37
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
import org.opensearch.common.io.stream.StreamInput;
1717
import org.opensearch.common.io.stream.StreamOutput;
1818
import org.opensearch.common.io.stream.Writeable;
19-
import org.opensearch.common.Strings;
20-
import org.opensearch.core.xcontent.XContentParser;
21-
22-
import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
2319

2420
/**
2521
* This class handles the dependent extensions information
@@ -60,39 +56,6 @@ public void writeTo(StreamOutput out) throws IOException {
6056
Version.writeVersion(version, out);
6157
}
6258

63-
public static ExtensionDependency parse(XContentParser parser) throws IOException {
64-
String uniqueId = null;
65-
Version version = null;
66-
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser);
67-
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
68-
String fieldName = parser.currentName();
69-
parser.nextToken();
70-
71-
switch (fieldName) {
72-
case UNIQUE_ID:
73-
uniqueId = parser.text();
74-
break;
75-
case VERSION:
76-
try {
77-
version = Version.fromString(parser.text());
78-
} catch (IllegalArgumentException e) {
79-
throw e;
80-
}
81-
break;
82-
default:
83-
parser.skipChildren();
84-
break;
85-
}
86-
}
87-
if (Strings.isNullOrEmpty(uniqueId)) {
88-
throw new IOException("Required field [uniqueId] is missing in the request for the dependent extension");
89-
} else if (version == null) {
90-
throw new IOException("Required field [version] is missing in the request for the dependent extension");
91-
}
92-
return new ExtensionDependency(uniqueId, version);
93-
94-
}
95-
9659
/**
9760
* The uniqueId of the dependency extension
9861
*

Diff for: server/src/main/java/org/opensearch/extensions/ExtensionsManager.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public static enum OpenSearchRequestType {
105105
/**
106106
* Instantiate a new ExtensionsManager object to handle requests and responses from extensions. This is called during Node bootstrap.
107107
*
108-
* @param additionalSettings Additional settings to read in from extensions.yml
108+
* @param additionalSettings Additional settings to read in from extension initialization request
109109
* @throws IOException If the extensions discovery file is not properly retrieved.
110110
*/
111111
public ExtensionsManager(Set<Setting<?>> additionalSettings) throws IOException {
@@ -503,4 +503,8 @@ void setAddSettingsUpdateConsumerRequestHandler(AddSettingsUpdateConsumerRequest
503503
Settings getEnvironmentSettings() {
504504
return environmentSettings;
505505
}
506+
507+
public Set<Setting<?>> getAdditionalSettings() {
508+
return this.additionalSettings;
509+
}
506510
}

Diff for: server/src/main/java/org/opensearch/extensions/rest/RestInitializeExtensionAction.java

+81-30
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88

99
package org.opensearch.extensions.rest;
1010

11+
import org.opensearch.Version;
1112
import org.opensearch.client.node.NodeClient;
13+
import org.opensearch.common.Strings;
14+
import org.opensearch.common.collect.Tuple;
15+
import org.opensearch.common.settings.Settings;
16+
import org.opensearch.common.xcontent.XContentHelper;
17+
import org.opensearch.core.xcontent.MediaType;
1218
import org.opensearch.core.xcontent.XContentBuilder;
13-
import org.opensearch.core.xcontent.XContentParser;
1419
import org.opensearch.extensions.ExtensionDependency;
1520
import org.opensearch.extensions.ExtensionScopedSettings;
1621
import org.opensearch.extensions.ExtensionsManager;
@@ -23,12 +28,16 @@
2328

2429
import java.io.IOException;
2530
import java.util.ArrayList;
26-
import java.util.Collections;
31+
import java.util.Arrays;
32+
import java.util.Collection;
33+
import java.util.HashMap;
2734
import java.util.List;
35+
import java.util.Map;
36+
import java.util.Set;
2837
import java.util.concurrent.CompletionException;
2938
import java.util.concurrent.TimeoutException;
39+
import java.util.stream.Collectors;
3040

31-
import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
3241
import static org.opensearch.rest.RestRequest.Method.POST;
3342

3443
/**
@@ -62,36 +71,79 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client
6271
String openSearchVersion = null;
6372
String minimumCompatibleVersion = null;
6473
List<ExtensionDependency> dependencies = new ArrayList<>();
74+
Set<String> additionalSettingsKeys = extensionsManager.getAdditionalSettings()
75+
.stream()
76+
.map(s -> s.getKey())
77+
.collect(Collectors.toSet());
6578

66-
try (XContentParser parser = request.contentParser()) {
67-
parser.nextToken();
68-
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser);
69-
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
70-
String currentFieldName = parser.currentName();
71-
parser.nextToken();
72-
if ("name".equals(currentFieldName)) {
73-
name = parser.text();
74-
} else if ("uniqueId".equals(currentFieldName)) {
75-
uniqueId = parser.text();
76-
} else if ("hostAddress".equals(currentFieldName)) {
77-
hostAddress = parser.text();
78-
} else if ("port".equals(currentFieldName)) {
79-
port = parser.text();
80-
} else if ("version".equals(currentFieldName)) {
81-
version = parser.text();
82-
} else if ("opensearchVersion".equals(currentFieldName)) {
83-
openSearchVersion = parser.text();
84-
} else if ("minimumCompatibleVersion".equals(currentFieldName)) {
85-
minimumCompatibleVersion = parser.text();
86-
} else if ("dependencies".equals(currentFieldName)) {
87-
ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.currentToken(), parser);
88-
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
89-
dependencies.add(ExtensionDependency.parse(parser));
79+
Tuple<? extends MediaType, Map<String, Object>> unreadExtensionTuple = XContentHelper.convertToMap(
80+
request.content(),
81+
false,
82+
request.getXContentType().xContent().mediaType()
83+
);
84+
Map<String, Object> extensionMap = unreadExtensionTuple.v2();
85+
86+
ExtensionScopedSettings extAdditionalSettings = new ExtensionScopedSettings(extensionsManager.getAdditionalSettings());
87+
88+
try {
89+
// checking to see whether any required fields are missing from extension initialization request or not
90+
String[] requiredFields = {
91+
"name",
92+
"uniqueId",
93+
"hostAddress",
94+
"port",
95+
"version",
96+
"opensearchVersion",
97+
"minimumCompatibleVersion" };
98+
List<String> missingFields = Arrays.stream(requiredFields)
99+
.filter(field -> !extensionMap.containsKey(field))
100+
.collect(Collectors.toList());
101+
if (!missingFields.isEmpty()) {
102+
throw new IOException("Extension is missing these required fields : " + missingFields);
103+
}
104+
105+
// Parse extension dependencies
106+
List<ExtensionDependency> extensionDependencyList = new ArrayList<ExtensionDependency>();
107+
if (extensionMap.get("dependencies") != null) {
108+
List<HashMap<String, ?>> extensionDependencies = new ArrayList<>(
109+
(Collection<HashMap<String, ?>>) extensionMap.get("dependencies")
110+
);
111+
for (HashMap<String, ?> dependency : extensionDependencies) {
112+
if (Strings.isNullOrEmpty((String) dependency.get("uniqueId"))) {
113+
throw new IOException("Required field [uniqueId] is missing in the request for the dependent extension");
114+
} else if (dependency.get("version") == null) {
115+
throw new IOException("Required field [version] is missing in the request for the dependent extension");
90116
}
117+
extensionDependencyList.add(
118+
new ExtensionDependency(
119+
dependency.get("uniqueId").toString(),
120+
Version.fromString(dependency.get("version").toString())
121+
)
122+
);
91123
}
92124
}
125+
126+
Map<String, ?> additionalSettingsMap = extensionMap.entrySet()
127+
.stream()
128+
.filter(kv -> additionalSettingsKeys.contains(kv.getKey()))
129+
.collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
130+
131+
Settings.Builder output = Settings.builder();
132+
output.loadFromMap(additionalSettingsMap);
133+
extAdditionalSettings.applySettings(output.build());
134+
135+
// Create extension read from initialization request
136+
name = extensionMap.get("name").toString();
137+
uniqueId = extensionMap.get("uniqueId").toString();
138+
hostAddress = extensionMap.get("hostAddress").toString();
139+
port = extensionMap.get("port").toString();
140+
version = extensionMap.get("version").toString();
141+
openSearchVersion = extensionMap.get("opensearchVersion").toString();
142+
minimumCompatibleVersion = extensionMap.get("minimumCompatibleVersion").toString();
143+
dependencies = extensionDependencyList;
93144
} catch (IOException e) {
94-
throw new IOException("Missing attribute", e);
145+
logger.warn("loading extension has been failed because of exception : " + e.getMessage());
146+
return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage()));
95147
}
96148

97149
Extension extension = new Extension(
@@ -103,8 +155,7 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client
103155
openSearchVersion,
104156
minimumCompatibleVersion,
105157
dependencies,
106-
// TODO add this to the API (https://github.com/opensearch-project/OpenSearch/issues/8032)
107-
new ExtensionScopedSettings(Collections.emptySet())
158+
extAdditionalSettings
108159
);
109160
try {
110161
extensionsManager.loadExtension(extension);

Diff for: server/src/test/java/org/opensearch/extensions/ExtensionsManagerTests.java

-12
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545
import org.opensearch.client.node.NodeClient;
4646
import org.opensearch.cluster.ClusterSettingsResponse;
4747
import org.opensearch.common.util.FeatureFlags;
48-
import org.opensearch.common.xcontent.json.JsonXContent;
49-
import org.opensearch.core.xcontent.XContentParser;
5048
import org.opensearch.env.EnvironmentSettingsResponse;
5149
import org.opensearch.cluster.metadata.IndexMetadata;
5250
import org.opensearch.cluster.node.DiscoveryNode;
@@ -400,16 +398,6 @@ public void testExtensionDependency() throws Exception {
400398
}
401399
}
402400

403-
public void testParseExtensionDependency() throws Exception {
404-
XContentParser parser = createParser(JsonXContent.jsonXContent, "{\"uniqueId\": \"test1\", \"version\": \"2.0.0\"}");
405-
406-
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
407-
ExtensionDependency dependency = ExtensionDependency.parse(parser);
408-
409-
assertEquals("test1", dependency.getUniqueId());
410-
assertEquals(Version.fromString("2.0.0"), dependency.getVersion());
411-
}
412-
413401
public void testInitialize() throws Exception {
414402
ExtensionsManager extensionsManager = new ExtensionsManager(Set.of());
415403

0 commit comments

Comments
 (0)