Skip to content

Commit c78697b

Browse files
mgodwanwangdongyu.danny
authored and
wangdongyu.danny
committed
SPI for loading ABC templates (opensearch-project#14659)
* SPI for loading ABC templates Signed-off-by: mgodwan <[email protected]>
1 parent f57cc7a commit c78697b

19 files changed

+886
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1818
- Create SystemIndexRegistry with helper method matchesSystemIndex ([#14415](https://github.com/opensearch-project/OpenSearch/pull/14415))
1919
- Print reason why parent task was cancelled ([#14604](https://github.com/opensearch-project/OpenSearch/issues/14604))
2020
- Add matchesPluginSystemIndexPattern to SystemIndexRegistry ([#14750](https://github.com/opensearch-project/OpenSearch/pull/14750))
21+
- Add Plugin interface for loading application based configuration templates (([#14659](https://github.com/opensearch-project/OpenSearch/issues/14659)))
2122

2223
### Dependencies
2324
- Bump `org.gradle.test-retry` from 1.5.8 to 1.5.9 ([#13442](https://github.com/opensearch-project/OpenSearch/pull/13442))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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.cluster.applicationtemplates;
10+
11+
import org.apache.logging.log4j.LogManager;
12+
import org.apache.logging.log4j.Logger;
13+
import org.opensearch.OpenSearchCorruptionException;
14+
import org.opensearch.action.admin.indices.template.put.PutComponentTemplateAction;
15+
import org.opensearch.client.Client;
16+
import org.opensearch.client.OriginSettingClient;
17+
import org.opensearch.cluster.ClusterState;
18+
import org.opensearch.cluster.metadata.ComponentTemplate;
19+
import org.opensearch.common.annotation.ExperimentalApi;
20+
import org.opensearch.common.unit.TimeValue;
21+
import org.opensearch.common.xcontent.json.JsonXContent;
22+
import org.opensearch.core.xcontent.DeprecationHandler;
23+
import org.opensearch.core.xcontent.NamedXContentRegistry;
24+
import org.opensearch.core.xcontent.XContentParser;
25+
26+
import java.io.IOException;
27+
import java.util.Objects;
28+
import java.util.function.Supplier;
29+
30+
/**
31+
* Class responsible for loading the component templates provided by a repository into the cluster state.
32+
*/
33+
@ExperimentalApi
34+
public class ClusterStateSystemTemplateLoader implements SystemTemplateLoader {
35+
36+
private final Client client;
37+
38+
private final Supplier<ClusterState> clusterStateSupplier;
39+
40+
private static final Logger logger = LogManager.getLogger(SystemTemplateLoader.class);
41+
42+
public static final String TEMPLATE_LOADER_IDENTIFIER = "system_template_loader";
43+
public static final String TEMPLATE_TYPE_KEY = "_type";
44+
45+
public ClusterStateSystemTemplateLoader(Client client, Supplier<ClusterState> clusterStateSupplier) {
46+
this.client = new OriginSettingClient(client, TEMPLATE_LOADER_IDENTIFIER);
47+
this.clusterStateSupplier = clusterStateSupplier;
48+
}
49+
50+
@Override
51+
public boolean loadTemplate(SystemTemplate template) throws IOException {
52+
final ComponentTemplate existingTemplate = clusterStateSupplier.get()
53+
.metadata()
54+
.componentTemplates()
55+
.get(template.templateMetadata().fullyQualifiedName());
56+
57+
if (existingTemplate != null
58+
&& !SystemTemplateMetadata.COMPONENT_TEMPLATE_TYPE.equals(
59+
Objects.toString(existingTemplate.metadata().get(TEMPLATE_TYPE_KEY))
60+
)) {
61+
throw new OpenSearchCorruptionException(
62+
"Attempting to create " + template.templateMetadata().name() + " which has already been created through some other source."
63+
);
64+
}
65+
66+
if (existingTemplate != null && existingTemplate.version() >= template.templateMetadata().version()) {
67+
logger.debug(
68+
"Skipping putting template {} as its existing version [{}] is >= fetched version [{}]",
69+
template.templateMetadata().fullyQualifiedName(),
70+
existingTemplate.version(),
71+
template.templateMetadata().version()
72+
);
73+
return false;
74+
}
75+
76+
ComponentTemplate newTemplate = null;
77+
try (
78+
XContentParser contentParser = JsonXContent.jsonXContent.createParser(
79+
NamedXContentRegistry.EMPTY,
80+
DeprecationHandler.IGNORE_DEPRECATIONS,
81+
template.templateContent().utf8ToString()
82+
)
83+
) {
84+
newTemplate = ComponentTemplate.parse(contentParser);
85+
}
86+
87+
if (!Objects.equals(newTemplate.version(), template.templateMetadata().version())) {
88+
throw new OpenSearchCorruptionException(
89+
"Template version mismatch for "
90+
+ template.templateMetadata().name()
91+
+ ". Version in metadata: "
92+
+ template.templateMetadata().version()
93+
+ " , Version in content: "
94+
+ newTemplate.version()
95+
);
96+
}
97+
98+
final PutComponentTemplateAction.Request request = new PutComponentTemplateAction.Request(
99+
template.templateMetadata().fullyQualifiedName()
100+
).componentTemplate(newTemplate);
101+
102+
return client.admin()
103+
.indices()
104+
.execute(PutComponentTemplateAction.INSTANCE, request)
105+
.actionGet(TimeValue.timeValueMillis(30000))
106+
.isAcknowledged();
107+
}
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.cluster.applicationtemplates;
10+
11+
import org.opensearch.common.annotation.ExperimentalApi;
12+
import org.opensearch.core.common.bytes.BytesReference;
13+
14+
/**
15+
* Encapsulates the information and content about a system template available within a repository.
16+
*/
17+
@ExperimentalApi
18+
public class SystemTemplate {
19+
20+
private final BytesReference templateContent;
21+
22+
private final SystemTemplateMetadata templateMetadata;
23+
24+
private final TemplateRepositoryMetadata repositoryMetadata;
25+
26+
public SystemTemplate(BytesReference templateContent, SystemTemplateMetadata templateInfo, TemplateRepositoryMetadata repositoryInfo) {
27+
this.templateContent = templateContent;
28+
this.templateMetadata = templateInfo;
29+
this.repositoryMetadata = repositoryInfo;
30+
}
31+
32+
public BytesReference templateContent() {
33+
return templateContent;
34+
}
35+
36+
public SystemTemplateMetadata templateMetadata() {
37+
return templateMetadata;
38+
}
39+
40+
public TemplateRepositoryMetadata repositoryMetadata() {
41+
return repositoryMetadata;
42+
}
43+
}
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.cluster.applicationtemplates;
10+
11+
import org.opensearch.common.annotation.ExperimentalApi;
12+
13+
import java.io.IOException;
14+
15+
/**
16+
* Interface to load template into the OpenSearch runtime.
17+
*/
18+
@ExperimentalApi
19+
public interface SystemTemplateLoader {
20+
21+
/**
22+
* @param template Templated to be loaded
23+
* @throws IOException If an exceptional situation is encountered while parsing/loading the template
24+
*/
25+
boolean loadTemplate(SystemTemplate template) throws IOException;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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.cluster.applicationtemplates;
10+
11+
import org.opensearch.common.annotation.ExperimentalApi;
12+
13+
/**
14+
* Metadata information about a template available in a template repository.
15+
*/
16+
@ExperimentalApi
17+
public class SystemTemplateMetadata {
18+
19+
private final long version;
20+
private final String type;
21+
private final String name;
22+
23+
private static final String DELIMITER = "@";
24+
25+
public static final String COMPONENT_TEMPLATE_TYPE = "@abc_template";
26+
27+
public SystemTemplateMetadata(long version, String type, String name) {
28+
this.version = version;
29+
this.type = type;
30+
this.name = name;
31+
}
32+
33+
public String type() {
34+
return type;
35+
}
36+
37+
public String name() {
38+
return name;
39+
}
40+
41+
public long version() {
42+
return version;
43+
}
44+
45+
/**
46+
* Gets the metadata using fully qualified name for the template
47+
* @param fullyQualifiedName (e.g. @abc_template@logs@1)
48+
* @return Metadata object based on name
49+
*/
50+
public static SystemTemplateMetadata fromComponentTemplate(String fullyQualifiedName) {
51+
assert fullyQualifiedName.length() > 1 : "System template name must have at least one component";
52+
assert fullyQualifiedName.substring(1, fullyQualifiedName.indexOf(DELIMITER, 1)).equals(COMPONENT_TEMPLATE_TYPE);
53+
54+
return new SystemTemplateMetadata(
55+
Long.parseLong(fullyQualifiedName.substring(fullyQualifiedName.lastIndexOf(DELIMITER))),
56+
COMPONENT_TEMPLATE_TYPE,
57+
fullyQualifiedName.substring(0, fullyQualifiedName.lastIndexOf(DELIMITER))
58+
);
59+
}
60+
61+
public static SystemTemplateMetadata fromComponentTemplateInfo(String name, long version) {
62+
return new SystemTemplateMetadata(version, COMPONENT_TEMPLATE_TYPE, name);
63+
}
64+
65+
public final String fullyQualifiedName() {
66+
return type + DELIMITER + name + DELIMITER + version;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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.cluster.applicationtemplates;
10+
11+
import org.opensearch.common.annotation.ExperimentalApi;
12+
13+
import java.io.IOException;
14+
15+
/**
16+
* Repository interface around the templates provided by a store (e.g. code repo, remote file store, etc)
17+
*/
18+
@ExperimentalApi
19+
public interface SystemTemplateRepository extends AutoCloseable {
20+
21+
/**
22+
* @return Metadata about the repository
23+
*/
24+
TemplateRepositoryMetadata metadata();
25+
26+
/**
27+
* @return Metadata for all available templates
28+
*/
29+
Iterable<SystemTemplateMetadata> listTemplates() throws IOException;
30+
31+
/**
32+
*
33+
* @param template metadata about template to be fetched
34+
* @return The actual template content
35+
*/
36+
SystemTemplate getTemplate(SystemTemplateMetadata template) throws IOException;
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.cluster.applicationtemplates;
10+
11+
import org.opensearch.common.annotation.ExperimentalApi;
12+
13+
import java.io.IOException;
14+
15+
/**
16+
* Plugin interface to expose the template maintaining logic.
17+
*/
18+
@ExperimentalApi
19+
public interface SystemTemplatesPlugin {
20+
21+
/**
22+
* @return repository implementation from which templates are to be fetched.
23+
*/
24+
SystemTemplateRepository loadRepository() throws IOException;
25+
26+
/**
27+
* @param templateInfo Metadata about the template to load
28+
* @return Implementation of TemplateLoader which determines how to make the template available at runtime.
29+
*/
30+
SystemTemplateLoader loaderFor(SystemTemplateMetadata templateInfo);
31+
}

0 commit comments

Comments
 (0)