[7.x] Add REST APIs for IndexTemplateV2Metadata CRUD (#54039) (#54347)

* Add REST APIs for IndexTemplateV2Metadata CRUD (#54039)

* Add REST APIs for IndexTemplateV2Metadata CRUD

This commit adds the get/put/delete APIs for interacting with the now v2 versions of index
templates.

These APIs are behind the existing `es.itv2_feature_flag_registered` system property feature flag.

Relates to #53101

* Add exceptions for HLRC tests

* Add skips for 7.x versions

* Use index_template instead of template_v2 in action names

* Add test for MetaDataIndexTemplateService.addIndexTemplateV2

* Move removal to static method and add test

* Add unit tests for request classes (implement hashCode & equals)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

* Fix compilation

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Lee Hinman 2020-03-27 10:47:22 -06:00 committed by GitHub
parent 1690e78646
commit f2cc2b1127
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1597 additions and 4 deletions

View File

@ -801,7 +801,9 @@ public class RestHighLevelClientTests extends ESTestCase {
"cluster.delete_component_template", "cluster.delete_component_template",
"indices.create_data_stream", "indices.create_data_stream",
"indices.get_data_streams", "indices.get_data_streams",
"indices.delete_data_stream" "indices.delete_data_stream",
"indices.put_index_template",
"indices.delete_index_template"
}; };
//These API are not required for high-level client feature completeness //These API are not required for high-level client feature completeness
String[] notRequiredApi = new String[] { String[] notRequiredApi = new String[] {

View File

@ -0,0 +1,35 @@
{
"indices.delete_index_template":{
"documentation":{
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html",
"description":"Deletes an index template."
},
"stability":"stable",
"url":{
"paths":[
{
"path":"/_index_template/{name}",
"methods":[
"DELETE"
],
"parts":{
"name":{
"type":"string",
"description":"The name of the template"
}
}
}
]
},
"params":{
"timeout":{
"type":"time",
"description":"Explicit operation timeout"
},
"master_timeout":{
"type":"time",
"description":"Specify timeout for connection to master"
}
}
}
}

View File

@ -0,0 +1,45 @@
{
"indices.get_index_template":{
"documentation":{
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html",
"description":"Returns an index template."
},
"stability":"stable",
"url":{
"paths":[
{
"path":"/_index_template",
"methods":[
"GET"
]
},
{
"path":"/_index_template/{name}",
"methods":[
"GET"
],
"parts":{
"name":{
"type":"list",
"description":"The comma separated names of the index templates"
}
}
}
]
},
"params":{
"flat_settings":{
"type":"boolean",
"description":"Return settings in flat format (default: false)"
},
"master_timeout":{
"type":"time",
"description":"Explicit operation timeout for connection to master node"
},
"local":{
"type":"boolean",
"description":"Return local information, do not retrieve the state from master node (default: false)"
}
}
}
}

View File

@ -0,0 +1,45 @@
{
"indices.put_index_template":{
"documentation":{
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html",
"description":"Creates or updates an index template."
},
"stability":"stable",
"url":{
"paths":[
{
"path":"/_index_template/{name}",
"methods":[
"PUT",
"POST"
],
"parts":{
"name":{
"type":"string",
"description":"The name of the template"
}
}
}
]
},
"params":{
"order":{
"type":"number",
"description":"The order for this template when merging multiple matching ones (higher numbers are merged later, overriding the lower numbers)"
},
"create":{
"type":"boolean",
"description":"Whether the index template should only be added if new or can also replace an existing one",
"default":false
},
"master_timeout":{
"type":"time",
"description":"Specify timeout for connection to master"
}
},
"body":{
"description":"The template definition",
"required":true
}
}
}

View File

@ -0,0 +1,66 @@
setup:
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.put_index_template:
name: test
body:
index_patterns: test-*
template:
settings:
number_of_shards: 1
number_of_replicas: 0
mappings:
properties:
field:
type: keyword
---
"Get index template":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.get_index_template:
name: test
- match: {index_templates.0.name: test}
- match: {index_templates.0.index_template.index_patterns: ["test-*"]}
- match: {index_templates.0.index_template.template.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}}
- match: {index_templates.0.index_template.template.mappings: {properties: {field: {type: keyword}}}}
---
"Get all tindex emplates":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.put_index_template:
name: test2
body:
index_patterns: test2-*
template:
settings:
number_of_shards: 1
- do:
indices.get_index_template: {}
- length: {index_templates: 2}
---
"Get index template with local flag":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.get_index_template:
name: test
local: true
- match: {index_templates.0.name: test}

View File

@ -0,0 +1,20 @@
setup:
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.delete_index_template:
name: '*'
ignore: 404
---
"Get missing template":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
catch: missing
indices.get_index_template:
name: test

View File

@ -0,0 +1,112 @@
---
"Put index template":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.put_index_template:
name: test
body:
index_patterns: test-*
template:
settings:
number_of_shards: 1
number_of_replicas: 0
mappings:
properties:
field:
type: keyword
- do:
indices.get_index_template:
name: test
- match: {index_templates.0.name: "test"}
- match: {index_templates.0.index_template.index_patterns: ["test-*"]}
- match: {index_templates.0.index_template.template.settings.index: {number_of_shards: '1', number_of_replicas: '0'}}
- match: {index_templates.0.index_template.template.mappings: {properties: {field: {type: keyword}}}}
---
"Put multiple index templates":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.put_index_template:
name: test
body:
index_patterns: [test-*, test2-*]
template:
settings:
number_of_shards: 1
number_of_replicas: 0
mappings:
properties:
field:
type: text
aliases:
test_alias: {}
test_blias: { routing: b }
test_clias: { filter: { term: { user: kimchy }}}
- do:
indices.get_index_template:
name: test
- match: {index_templates.0.index_template.index_patterns: ["test-*", "test2-*"]}
- match: {index_templates.0.index_template.template.settings.index: {number_of_shards: '1', number_of_replicas: '0'}}
- match: {index_templates.0.index_template.template.mappings: {properties: {field: {type: text}}}}
- length: {index_templates.0.index_template.template.aliases: 3}
- is_true: index_templates.0.index_template.template.aliases.test_alias
- match: {index_templates.0.index_template.template.aliases.test_blias.index_routing: "b" }
- match: {index_templates.0.index_template.template.aliases.test_blias.search_routing: "b" }
- match: {index_templates.0.index_template.template.aliases.test_clias.filter.term.user: "kimchy" }
---
"Put index template with 'create' flag":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
indices.put_index_template:
name: test2
body:
index_patterns: test-*
template:
settings:
number_of_shards: 1
number_of_replicas: 0
- do:
indices.get_index_template:
name: test2
- match: {index_templates.0.index_template.index_patterns: ["test-*"]}
- match: {index_templates.0.index_template.template.settings.index: {number_of_shards: '1', number_of_replicas: '0'}}
- do:
catch: bad_request
indices.put_index_template:
name: test2
create: true
body:
index_patterns: test-*
template:
settings:
number_of_shards: 1
number_of_replicas: 0
---
"Put index template without index_patterns":
- skip:
version: " - 7.7.99"
reason: "index template v2 API unavailable before 7.8"
- do:
catch: bad_request
indices.put_index_template:
name: test
body: {}

View File

@ -153,16 +153,22 @@ import org.elasticsearch.action.admin.indices.stats.IndicesStatsAction;
import org.elasticsearch.action.admin.indices.stats.TransportIndicesStatsAction; import org.elasticsearch.action.admin.indices.stats.TransportIndicesStatsAction;
import org.elasticsearch.action.admin.indices.template.delete.DeleteComponentTemplateAction; import org.elasticsearch.action.admin.indices.template.delete.DeleteComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateAction; import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateV2Action;
import org.elasticsearch.action.admin.indices.template.delete.TransportDeleteComponentTemplateAction; import org.elasticsearch.action.admin.indices.template.delete.TransportDeleteComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.delete.TransportDeleteIndexTemplateAction; import org.elasticsearch.action.admin.indices.template.delete.TransportDeleteIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.delete.TransportDeleteIndexTemplateV2Action;
import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction; import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplateV2Action;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction;
import org.elasticsearch.action.admin.indices.template.get.TransportGetComponentTemplateAction; import org.elasticsearch.action.admin.indices.template.get.TransportGetComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.get.TransportGetIndexTemplateV2Action;
import org.elasticsearch.action.admin.indices.template.get.TransportGetIndexTemplatesAction; import org.elasticsearch.action.admin.indices.template.get.TransportGetIndexTemplatesAction;
import org.elasticsearch.action.admin.indices.template.put.PutComponentTemplateAction; import org.elasticsearch.action.admin.indices.template.put.PutComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateV2Action;
import org.elasticsearch.action.admin.indices.template.put.TransportPutComponentTemplateAction; import org.elasticsearch.action.admin.indices.template.put.TransportPutComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.put.TransportPutIndexTemplateAction; import org.elasticsearch.action.admin.indices.template.put.TransportPutIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.put.TransportPutIndexTemplateV2Action;
import org.elasticsearch.action.admin.indices.upgrade.get.TransportUpgradeStatusAction; import org.elasticsearch.action.admin.indices.upgrade.get.TransportUpgradeStatusAction;
import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusAction; import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusAction;
import org.elasticsearch.action.admin.indices.upgrade.post.TransportUpgradeAction; import org.elasticsearch.action.admin.indices.upgrade.post.TransportUpgradeAction;
@ -287,12 +293,14 @@ import org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction;
import org.elasticsearch.rest.action.admin.indices.RestDeleteComponentTemplateAction; import org.elasticsearch.rest.action.admin.indices.RestDeleteComponentTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestDeleteIndexAction; import org.elasticsearch.rest.action.admin.indices.RestDeleteIndexAction;
import org.elasticsearch.rest.action.admin.indices.RestDeleteIndexTemplateAction; import org.elasticsearch.rest.action.admin.indices.RestDeleteIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestDeleteIndexTemplateV2Action;
import org.elasticsearch.rest.action.admin.indices.RestFlushAction; import org.elasticsearch.rest.action.admin.indices.RestFlushAction;
import org.elasticsearch.rest.action.admin.indices.RestForceMergeAction; import org.elasticsearch.rest.action.admin.indices.RestForceMergeAction;
import org.elasticsearch.rest.action.admin.indices.RestGetAliasesAction; import org.elasticsearch.rest.action.admin.indices.RestGetAliasesAction;
import org.elasticsearch.rest.action.admin.indices.RestGetComponentTemplateAction; import org.elasticsearch.rest.action.admin.indices.RestGetComponentTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestGetFieldMappingAction; import org.elasticsearch.rest.action.admin.indices.RestGetFieldMappingAction;
import org.elasticsearch.rest.action.admin.indices.RestGetIndexTemplateAction; import org.elasticsearch.rest.action.admin.indices.RestGetIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestGetIndexTemplateV2Action;
import org.elasticsearch.rest.action.admin.indices.RestGetIndicesAction; import org.elasticsearch.rest.action.admin.indices.RestGetIndicesAction;
import org.elasticsearch.rest.action.admin.indices.RestGetMappingAction; import org.elasticsearch.rest.action.admin.indices.RestGetMappingAction;
import org.elasticsearch.rest.action.admin.indices.RestGetSettingsAction; import org.elasticsearch.rest.action.admin.indices.RestGetSettingsAction;
@ -305,6 +313,7 @@ import org.elasticsearch.rest.action.admin.indices.RestIndicesStatsAction;
import org.elasticsearch.rest.action.admin.indices.RestOpenIndexAction; import org.elasticsearch.rest.action.admin.indices.RestOpenIndexAction;
import org.elasticsearch.rest.action.admin.indices.RestPutComponentTemplateAction; import org.elasticsearch.rest.action.admin.indices.RestPutComponentTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestPutIndexTemplateAction; import org.elasticsearch.rest.action.admin.indices.RestPutIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestPutIndexTemplateV2Action;
import org.elasticsearch.rest.action.admin.indices.RestPutMappingAction; import org.elasticsearch.rest.action.admin.indices.RestPutMappingAction;
import org.elasticsearch.rest.action.admin.indices.RestRecoveryAction; import org.elasticsearch.rest.action.admin.indices.RestRecoveryAction;
import org.elasticsearch.rest.action.admin.indices.RestRefreshAction; import org.elasticsearch.rest.action.admin.indices.RestRefreshAction;
@ -549,6 +558,9 @@ public class ActionModule extends AbstractModule {
actions.register(PutComponentTemplateAction.INSTANCE, TransportPutComponentTemplateAction.class); actions.register(PutComponentTemplateAction.INSTANCE, TransportPutComponentTemplateAction.class);
actions.register(GetComponentTemplateAction.INSTANCE, TransportGetComponentTemplateAction.class); actions.register(GetComponentTemplateAction.INSTANCE, TransportGetComponentTemplateAction.class);
actions.register(DeleteComponentTemplateAction.INSTANCE, TransportDeleteComponentTemplateAction.class); actions.register(DeleteComponentTemplateAction.INSTANCE, TransportDeleteComponentTemplateAction.class);
actions.register(PutIndexTemplateV2Action.INSTANCE, TransportPutIndexTemplateV2Action.class);
actions.register(GetIndexTemplateV2Action.INSTANCE, TransportGetIndexTemplateV2Action.class);
actions.register(DeleteIndexTemplateV2Action.INSTANCE, TransportDeleteIndexTemplateV2Action.class);
} }
actions.register(ValidateQueryAction.INSTANCE, TransportValidateQueryAction.class); actions.register(ValidateQueryAction.INSTANCE, TransportValidateQueryAction.class);
actions.register(RefreshAction.INSTANCE, TransportRefreshAction.class); actions.register(RefreshAction.INSTANCE, TransportRefreshAction.class);
@ -688,6 +700,9 @@ public class ActionModule extends AbstractModule {
registerHandler.accept(new RestPutComponentTemplateAction()); registerHandler.accept(new RestPutComponentTemplateAction());
registerHandler.accept(new RestGetComponentTemplateAction()); registerHandler.accept(new RestGetComponentTemplateAction());
registerHandler.accept(new RestDeleteComponentTemplateAction()); registerHandler.accept(new RestDeleteComponentTemplateAction());
registerHandler.accept(new RestPutIndexTemplateV2Action());
registerHandler.accept(new RestGetIndexTemplateV2Action());
registerHandler.accept(new RestDeleteIndexTemplateV2Action());
} }
registerHandler.accept(new RestPutMappingAction()); registerHandler.accept(new RestPutMappingAction());

View File

@ -0,0 +1,108 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.delete;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
public class DeleteIndexTemplateV2Action extends ActionType<AcknowledgedResponse> {
public static final DeleteIndexTemplateV2Action INSTANCE = new DeleteIndexTemplateV2Action();
public static final String NAME = "indices:admin/index_template/delete";
private DeleteIndexTemplateV2Action() {
super(NAME, AcknowledgedResponse::new);
}
public static class Request extends MasterNodeRequest<Request> {
private String name;
public Request(StreamInput in) throws IOException {
super(in);
name = in.readString();
}
public Request() { }
/**
* Constructs a new delete template request for the specified name.
*/
public Request(String name) {
this.name = name;
}
/**
* Set the index template name to delete.
*/
public Request name(String name) {
this.name = name;
return this;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (name == null) {
validationException = addValidationError("name is missing", validationException);
}
return validationException;
}
/**
* The index template name to delete.
*/
public String name() {
return name;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(name);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Request other = (Request) obj;
return Objects.equals(other.name, this.name);
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.delete;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.io.IOException;
public class TransportDeleteIndexTemplateV2Action
extends TransportMasterNodeAction<DeleteIndexTemplateV2Action.Request, AcknowledgedResponse> {
private static final Logger logger = LogManager.getLogger(TransportDeleteIndexTemplateV2Action.class);
private final MetaDataIndexTemplateService indexTemplateService;
@Inject
public TransportDeleteIndexTemplateV2Action(TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, MetaDataIndexTemplateService indexTemplateService,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
super(DeleteIndexTemplateV2Action.NAME, transportService, clusterService, threadPool, actionFilters,
DeleteIndexTemplateV2Action.Request::new, indexNameExpressionResolver);
this.indexTemplateService = indexTemplateService;
}
@Override
protected String executor() {
// we go async right away
return ThreadPool.Names.SAME;
}
@Override
protected AcknowledgedResponse read(StreamInput in) throws IOException {
return new AcknowledgedResponse(in);
}
@Override
protected ClusterBlockException checkBlock(DeleteIndexTemplateV2Action.Request request, ClusterState state) {
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE);
}
@Override
protected void masterOperation(final DeleteIndexTemplateV2Action.Request request, final ClusterState state,
final ActionListener<AcknowledgedResponse> listener) {
indexTemplateService.removeIndexTemplateV2(request.name(), request.masterNodeTimeout(), listener);
}
}

View File

@ -0,0 +1,186 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.get;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
import org.elasticsearch.cluster.metadata.IndexTemplateV2;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
public class GetIndexTemplateV2Action extends ActionType<GetIndexTemplateV2Action.Response> {
public static final GetIndexTemplateV2Action INSTANCE = new GetIndexTemplateV2Action();
public static final String NAME = "indices:admin/index_template/get";
private GetIndexTemplateV2Action() {
super(NAME, GetIndexTemplateV2Action.Response::new);
}
/**
* Request that to retrieve one or more index templates
*/
public static class Request extends MasterNodeReadRequest<Request> {
private String[] names;
public Request() { }
public Request(String... names) {
this.names = names;
}
public Request(StreamInput in) throws IOException {
super(in);
names = in.readStringArray();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeStringArray(names);
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (names == null) {
validationException = addValidationError("names is null or empty", validationException);
} else {
for (String name : names) {
if (name == null || Strings.hasText(name) == false) {
validationException = addValidationError("name is missing", validationException);
}
}
}
return validationException;
}
/**
* Sets the names of the index templates.
*/
public Request names(String... names) {
this.names = names;
return this;
}
/**
* The names of the index templates.
*/
public String[] names() {
return this.names;
}
@Override
public int hashCode() {
return Arrays.hashCode(names);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Request other = (Request) obj;
return Arrays.equals(other.names, this.names);
}
}
public static class Response extends ActionResponse implements ToXContentObject {
public static final ParseField NAME = new ParseField("name");
public static final ParseField INDEX_TEMPLATES = new ParseField("index_templates");
public static final ParseField INDEX_TEMPLATE = new ParseField("index_template");
private final Map<String, IndexTemplateV2> indexTemplates;
public Response(StreamInput in) throws IOException {
super(in);
int size = in.readVInt();
indexTemplates = new HashMap<>();
for (int i = 0 ; i < size ; i++) {
indexTemplates.put(in.readString(), new IndexTemplateV2(in));
}
}
public Response(Map<String, IndexTemplateV2> indexTemplates) {
this.indexTemplates = indexTemplates;
}
public Map<String, IndexTemplateV2> indexTemplates() {
return indexTemplates;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(indexTemplates.size());
for (Map.Entry<String, IndexTemplateV2> indexTemplate : indexTemplates.entrySet()) {
out.writeString(indexTemplate.getKey());
indexTemplate.getValue().writeTo(out);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GetIndexTemplateV2Action.Response that = (GetIndexTemplateV2Action.Response) o;
return Objects.equals(indexTemplates, that.indexTemplates);
}
@Override
public int hashCode() {
return Objects.hash(indexTemplates);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startArray(INDEX_TEMPLATES.getPreferredName());
for (Map.Entry<String, IndexTemplateV2> indexTemplate : this.indexTemplates.entrySet()) {
builder.startObject();
builder.field(NAME.getPreferredName(), indexTemplate.getKey());
builder.field(INDEX_TEMPLATE.getPreferredName(), indexTemplate.getValue());
builder.endObject();
}
builder.endArray();
builder.endObject();
return builder;
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.get;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexTemplateV2;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class TransportGetIndexTemplateV2Action
extends TransportMasterNodeReadAction<GetIndexTemplateV2Action.Request, GetIndexTemplateV2Action.Response> {
@Inject
public TransportGetIndexTemplateV2Action(TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver) {
super(GetIndexTemplateV2Action.NAME, transportService, clusterService, threadPool, actionFilters,
GetIndexTemplateV2Action.Request::new, indexNameExpressionResolver);
}
@Override
protected String executor() {
return ThreadPool.Names.SAME;
}
@Override
protected GetIndexTemplateV2Action.Response read(StreamInput in) throws IOException {
return new GetIndexTemplateV2Action.Response(in);
}
@Override
protected ClusterBlockException checkBlock(GetIndexTemplateV2Action.Request request, ClusterState state) {
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ);
}
@Override
protected void masterOperation(GetIndexTemplateV2Action.Request request, ClusterState state,
ActionListener<GetIndexTemplateV2Action.Response> listener) {
Map<String, IndexTemplateV2> allTemplates = state.metaData().templatesV2();
// If we did not ask for a specific name, then we return all templates
if (request.names().length == 0) {
listener.onResponse(new GetIndexTemplateV2Action.Response(allTemplates));
return;
}
final Map<String, IndexTemplateV2> results = new HashMap<>();
for (String name : request.names()) {
if (Regex.isSimpleMatchPattern(name)) {
for (Map.Entry<String, IndexTemplateV2> entry : allTemplates.entrySet()) {
if (Regex.simpleMatch(name, entry.getKey())) {
results.put(entry.getKey(), entry.getValue());
}
}
} else if (allTemplates.containsKey(name)) {
results.put(name, allTemplates.get(name));
}
}
listener.onResponse(new GetIndexTemplateV2Action.Response(results));
}
}

View File

@ -0,0 +1,181 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.put;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.cluster.metadata.IndexTemplateV2;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
public class PutIndexTemplateV2Action extends ActionType<AcknowledgedResponse> {
public static final PutIndexTemplateV2Action INSTANCE = new PutIndexTemplateV2Action();
public static final String NAME = "indices:admin/index_template/put";
private PutIndexTemplateV2Action() {
super(NAME, AcknowledgedResponse::new);
}
/**
* A request for putting a single index template into the cluster state
*/
public static class Request extends MasterNodeRequest<Request> implements IndicesRequest {
private final String name;
@Nullable
private String cause;
private boolean create;
private IndexTemplateV2 indexTemplate;
public Request(StreamInput in) throws IOException {
super(in);
this.name = in.readString();
this.cause = in.readOptionalString();
this.create = in.readBoolean();
this.indexTemplate = new IndexTemplateV2(in);
}
/**
* Constructs a new put index template request with the provided name.
*/
public Request(String name) {
this.name = name;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(name);
out.writeOptionalString(cause);
out.writeBoolean(create);
this.indexTemplate.writeTo(out);
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (name == null || Strings.hasText(name) == false) {
validationException = addValidationError("name is missing", validationException);
}
if (indexTemplate == null) {
validationException = addValidationError("an index template is required", validationException);
}
return validationException;
}
/**
* The name of the index template.
*/
public String name() {
return this.name;
}
/**
* Set to {@code true} to force only creation, not an update of an index template. If it already
* exists, it will fail with an {@link IllegalArgumentException}.
*/
public Request create(boolean create) {
this.create = create;
return this;
}
public boolean create() {
return create;
}
/**
* The cause for this index template creation.
*/
public Request cause(@Nullable String cause) {
this.cause = cause;
return this;
}
@Nullable
public String cause() {
return this.cause;
}
/**
* The index template that will be inserted into the cluster state
*/
public Request indexTemplate(IndexTemplateV2 template) {
this.indexTemplate = template;
return this;
}
public IndexTemplateV2 indexTemplate() {
return this.indexTemplate;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("PutTemplateV2Request[");
sb.append("name=").append(name);
sb.append(", cause=").append(cause);
sb.append(", create=").append(create);
sb.append(", index_template=").append(indexTemplate);
sb.append("]");
return sb.toString();
}
@Override
public String[] indices() {
return indexTemplate.indexPatterns().toArray(Strings.EMPTY_ARRAY);
}
@Override
public IndicesOptions indicesOptions() {
return IndicesOptions.strictExpand();
}
@Override
public int hashCode() {
return Objects.hash(name, cause, create, indexTemplate);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Request other = (Request) obj;
return Objects.equals(this.name, other.name) &&
Objects.equals(this.cause, other.cause) &&
Objects.equals(this.indexTemplate, other.indexTemplate) &&
this.create == other.create;
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.put;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexTemplateV2;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
import org.elasticsearch.cluster.metadata.Template;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.io.IOException;
public class TransportPutIndexTemplateV2Action
extends TransportMasterNodeAction<PutIndexTemplateV2Action.Request, AcknowledgedResponse> {
private final MetaDataIndexTemplateService indexTemplateService;
@Inject
public TransportPutIndexTemplateV2Action(TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, MetaDataIndexTemplateService indexTemplateService,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
super(PutIndexTemplateV2Action.NAME, transportService, clusterService, threadPool, actionFilters,
PutIndexTemplateV2Action.Request::new, indexNameExpressionResolver);
this.indexTemplateService = indexTemplateService;
}
@Override
protected String executor() {
// we go async right away
return ThreadPool.Names.SAME;
}
@Override
protected AcknowledgedResponse read(StreamInput in) throws IOException {
return new AcknowledgedResponse(in);
}
@Override
protected ClusterBlockException checkBlock(PutIndexTemplateV2Action.Request request, ClusterState state) {
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE);
}
@Override
protected void masterOperation(final PutIndexTemplateV2Action.Request request, final ClusterState state,
final ActionListener<AcknowledgedResponse> listener) {
IndexTemplateV2 indexTemplate = request.indexTemplate();
Template template = indexTemplate.template();
// Normalize the index settings if necessary
if (template.settings() != null) {
Settings.Builder settings = Settings.builder().put(template.settings()).normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);
template = new Template(settings.build(), template.mappings(), template.aliases());
indexTemplate = new IndexTemplateV2(indexTemplate.indexPatterns(), template, indexTemplate.composedOf(),
indexTemplate.priority(), indexTemplate.version(), indexTemplate.metadata());
}
indexTemplateService.putIndexTemplateV2(request.cause(), request.create(), request.name(), request.masterNodeTimeout(),
indexTemplate, listener);
}
}

View File

@ -241,6 +241,108 @@ public class MetaDataIndexTemplateService {
}); });
} }
/**
* Add the given component template to the cluster state. If {@code create} is true, an
* exception will be thrown if the component template already exists
*/
public void putIndexTemplateV2(final String cause, final boolean create, final String name, final TimeValue masterTimeout,
final IndexTemplateV2 template, final ActionListener<AcknowledgedResponse> listener) {
clusterService.submitStateUpdateTask("create-index-template-v2 [" + name + "], cause [" + cause + "]",
new ClusterStateUpdateTask(Priority.URGENT) {
@Override
public TimeValue timeout() {
return masterTimeout;
}
@Override
public void onFailure(String source, Exception e) {
listener.onFailure(e);
}
@Override
public ClusterState execute(ClusterState currentState) {
return addIndexTemplateV2(currentState, create, name, template);
}
@Override
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
listener.onResponse(new AcknowledgedResponse(true));
}
});
}
// Package visible for testing
static ClusterState addIndexTemplateV2(final ClusterState currentState, final boolean create,
final String name, final IndexTemplateV2 template) {
if (create && currentState.metaData().templatesV2().containsKey(name)) {
throw new IllegalArgumentException("index template [" + name + "] already exists");
}
// TODO: validation of index template
// validateAndAddTemplate(request, templateBuilder, indicesService, xContentRegistry);
logger.info("adding index template [{}]", name);
return ClusterState.builder(currentState)
.metaData(MetaData.builder(currentState.metaData()).put(name, template))
.build();
}
/**
* Remove the given index template from the cluster state. The index template name
* supports simple regex wildcards for removing multiple index templates at a time.
*/
public void removeIndexTemplateV2(final String name, final TimeValue masterTimeout,
final ActionListener<AcknowledgedResponse> listener) {
clusterService.submitStateUpdateTask("remove-index-template-v2 [" + name + "]",
new ClusterStateUpdateTask(Priority.URGENT) {
@Override
public TimeValue timeout() {
return masterTimeout;
}
@Override
public void onFailure(String source, Exception e) {
listener.onFailure(e);
}
@Override
public ClusterState execute(ClusterState currentState) {
return innerRemoveIndexTemplateV2(currentState, name);
}
@Override
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
listener.onResponse(new AcknowledgedResponse(true));
}
});
}
// Package visible for testing
static ClusterState innerRemoveIndexTemplateV2(ClusterState currentState, String name) {
Set<String> templateNames = new HashSet<>();
for (String templateName : currentState.metaData().templatesV2().keySet()) {
if (Regex.simpleMatch(name, templateName)) {
templateNames.add(templateName);
}
}
if (templateNames.isEmpty()) {
// if its a match all pattern, and no templates are found (we have none), don't
// fail with index missing...
if (Regex.isMatchAllPattern(name)) {
return currentState;
}
throw new IndexTemplateMissingException(name);
}
MetaData.Builder metaData = MetaData.builder(currentState.metaData());
for (String templateName : templateNames) {
logger.info("removing index template [{}]", templateName);
metaData.removeIndexTemplate(templateName);
}
return ClusterState.builder(currentState).metaData(metaData).build();
}
public void putTemplate(final PutRequest request, final PutListener listener) { public void putTemplate(final PutRequest request, final PutListener listener) {
Settings.Builder updatedSettingsBuilder = Settings.builder(); Settings.Builder updatedSettingsBuilder = Settings.builder();
updatedSettingsBuilder.put(request.settings).normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX); updatedSettingsBuilder.put(request.settings).normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);

View File

@ -0,0 +1,54 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.indices;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateV2Action;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
public class RestDeleteIndexTemplateV2Action extends BaseRestHandler {
@Override
public List<Route> routes() {
return Collections.singletonList(new Route(DELETE, "/_index_template/{name}"));
}
@Override
public String getName() {
return "delete_index_template_v2_action";
}
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
DeleteIndexTemplateV2Action.Request deleteReq = new DeleteIndexTemplateV2Action.Request(request.param("name"));
deleteReq.masterNodeTimeout(request.paramAsTime("master_timeout", deleteReq.masterNodeTimeout()));
return channel -> client.execute(DeleteIndexTemplateV2Action.INSTANCE, deleteReq, new RestToXContentListener<>(channel));
}
}

View File

@ -0,0 +1,83 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.indices;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplateV2Action;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK;
public class RestGetIndexTemplateV2Action extends BaseRestHandler {
@Override
public List<Route> routes() {
return Arrays.asList(
new Route(GET, "/_index_template"),
new Route(GET, "/_index_template/{name}"),
new Route(HEAD, "/_index_template/{name}"));
}
@Override
public String getName() {
return "get_index_template_v2_action";
}
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
final String[] names = Strings.splitStringByCommaToArray(request.param("name"));
final GetIndexTemplateV2Action.Request getRequest = new GetIndexTemplateV2Action.Request(names);
getRequest.local(request.paramAsBoolean("local", getRequest.local()));
getRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getRequest.masterNodeTimeout()));
final boolean implicitAll = getRequest.names().length == 0;
return channel ->
client.execute(GetIndexTemplateV2Action.INSTANCE, getRequest,
new RestToXContentListener<GetIndexTemplateV2Action.Response>(channel) {
@Override
protected RestStatus getStatus(final GetIndexTemplateV2Action.Response response) {
final boolean templateExists = response.indexTemplates().isEmpty() == false;
return (templateExists || implicitAll) ? OK : NOT_FOUND;
}
});
}
@Override
protected Set<String> responseParams() {
return Settings.FORMAT_PARAMS;
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.rest.action.admin.indices;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateV2Action;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.cluster.metadata.IndexTemplateV2;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestRequest.Method.PUT;
public class RestPutIndexTemplateV2Action extends BaseRestHandler {
@Override
public List<Route> routes() {
return Arrays.asList(
new Route(POST, "/_index_template/{name}"),
new Route(PUT, "/_index_template/{name}"));
}
@Override
public String getName() {
return "put_index_template_v2_action";
}
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
PutIndexTemplateV2Action.Request putRequest = new PutIndexTemplateV2Action.Request(request.param("name"));
putRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putRequest.masterNodeTimeout()));
putRequest.create(request.paramAsBoolean("create", false));
putRequest.cause(request.param("cause", "api"));
putRequest.indexTemplate(IndexTemplateV2.parse(request.contentParser()));
return channel -> client.execute(PutIndexTemplateV2Action.INSTANCE, putRequest, new RestToXContentListener<>(channel));
}
}

View File

@ -0,0 +1,42 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.delete;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import java.io.IOException;
public class DeleteIndexTemplateV2RequestTests extends AbstractWireSerializingTestCase<DeleteIndexTemplateV2Action.Request> {
@Override
protected Writeable.Reader<DeleteIndexTemplateV2Action.Request> instanceReader() {
return DeleteIndexTemplateV2Action.Request::new;
}
@Override
protected DeleteIndexTemplateV2Action.Request createTestInstance() {
return new DeleteIndexTemplateV2Action.Request(randomAlphaOfLength(5));
}
@Override
protected DeleteIndexTemplateV2Action.Request mutateInstance(DeleteIndexTemplateV2Action.Request instance) throws IOException {
return randomValueOtherThan(instance, this::createTestInstance);
}
}

View File

@ -0,0 +1,42 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.get;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import java.io.IOException;
public class GetIndexTemplateV2RequestTests extends AbstractWireSerializingTestCase<GetIndexTemplateV2Action.Request> {
@Override
protected Writeable.Reader<GetIndexTemplateV2Action.Request> instanceReader() {
return GetIndexTemplateV2Action.Request::new;
}
@Override
protected GetIndexTemplateV2Action.Request createTestInstance() {
return new GetIndexTemplateV2Action.Request(generateRandomStringArray(5, 5, false, false));
}
@Override
protected GetIndexTemplateV2Action.Request mutateInstance(GetIndexTemplateV2Action.Request instance) throws IOException {
return randomValueOtherThan(instance, this::createTestInstance);
}
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.get;
import org.elasticsearch.cluster.metadata.IndexTemplateV2;
import org.elasticsearch.cluster.metadata.IndexTemplateV2Tests;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class GetIndexTemplateV2ResponseTests extends AbstractWireSerializingTestCase<GetIndexTemplateV2Action.Response> {
@Override
protected Writeable.Reader<GetIndexTemplateV2Action.Response> instanceReader() {
return GetIndexTemplateV2Action.Response::new;
}
@Override
protected GetIndexTemplateV2Action.Response createTestInstance() {
if (randomBoolean()) {
return new GetIndexTemplateV2Action.Response(Collections.emptyMap());
}
Map<String, IndexTemplateV2> templates = new HashMap<>();
for (int i = 0; i < randomIntBetween(1, 4); i++) {
templates.put(randomAlphaOfLength(4), IndexTemplateV2Tests.randomInstance());
}
return new GetIndexTemplateV2Action.Response(templates);
}
@Override
protected GetIndexTemplateV2Action.Response mutateInstance(GetIndexTemplateV2Action.Response instance) throws IOException {
return randomValueOtherThan(instance, this::createTestInstance);
}
}

View File

@ -0,0 +1,47 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.template.put;
import org.elasticsearch.cluster.metadata.IndexTemplateV2Tests;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import java.io.IOException;
public class PutIndexTemplateV2RequestTests extends AbstractWireSerializingTestCase<PutIndexTemplateV2Action.Request> {
@Override
protected Writeable.Reader<PutIndexTemplateV2Action.Request> instanceReader() {
return PutIndexTemplateV2Action.Request::new;
}
@Override
protected PutIndexTemplateV2Action.Request createTestInstance() {
PutIndexTemplateV2Action.Request req = new PutIndexTemplateV2Action.Request(randomAlphaOfLength(4));
req.cause(randomAlphaOfLength(4));
req.create(randomBoolean());
req.indexTemplate(IndexTemplateV2Tests.randomInstance());
return req;
}
@Override
protected PutIndexTemplateV2Action.Request mutateInstance(PutIndexTemplateV2Action.Request instance) throws IOException {
return randomValueOtherThan(instance, this::createTestInstance);
}
}

View File

@ -32,6 +32,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.indices.IndexTemplateMissingException;
import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.indices.InvalidIndexTemplateException; import org.elasticsearch.indices.InvalidIndexTemplateException;
import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.ESSingleNodeTestCase;
@ -237,6 +238,37 @@ public class MetaDataIndexTemplateServiceTests extends ESSingleNodeTestCase {
() -> metaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo2", componentTemplate4)); () -> metaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo2", componentTemplate4));
} }
public void testAddIndexTemplateV2() {
ClusterState state = ClusterState.EMPTY_STATE;
IndexTemplateV2 template = IndexTemplateV2Tests.randomInstance();
state = MetaDataIndexTemplateService.addIndexTemplateV2(state, false, "foo", template);
assertNotNull(state.metaData().templatesV2().get("foo"));
assertThat(state.metaData().templatesV2().get("foo"), equalTo(template));
final ClusterState throwState = ClusterState.builder(state).build();
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> MetaDataIndexTemplateService.addIndexTemplateV2(throwState, true, "foo", template));
assertThat(e.getMessage(), containsString("index template [foo] already exists"));
state = MetaDataIndexTemplateService.addIndexTemplateV2(state, randomBoolean(), "bar", template);
assertNotNull(state.metaData().templatesV2().get("bar"));
}
public void testRemoveIndexTemplateV2() {
IndexTemplateV2 template = IndexTemplateV2Tests.randomInstance();
IndexTemplateMissingException e = expectThrows(IndexTemplateMissingException.class,
() -> MetaDataIndexTemplateService.innerRemoveIndexTemplateV2(ClusterState.EMPTY_STATE, "foo"));
assertThat(e.getMessage(), equalTo("index_template [foo] missing"));
final ClusterState state = MetaDataIndexTemplateService.addIndexTemplateV2(ClusterState.EMPTY_STATE, false, "foo", template);
assertNotNull(state.metaData().templatesV2().get("foo"));
assertThat(state.metaData().templatesV2().get("foo"), equalTo(template));
ClusterState updatedState = MetaDataIndexTemplateService.innerRemoveIndexTemplateV2(state, "foo");
assertNull(updatedState.metaData().templatesV2().get("foo"));
}
private static List<Throwable> putTemplate(NamedXContentRegistry xContentRegistry, PutRequest request) { private static List<Throwable> putTemplate(NamedXContentRegistry xContentRegistry, PutRequest request) {
MetaDataCreateIndexService createIndexService = new MetaDataCreateIndexService( MetaDataCreateIndexService createIndexService = new MetaDataCreateIndexService(
Settings.EMPTY, Settings.EMPTY,

View File

@ -58,7 +58,7 @@ public class ClusterPrivilegeResolver {
private static final Set<String> MONITOR_WATCHER_PATTERN = Collections.singleton("cluster:monitor/xpack/watcher/*"); private static final Set<String> MONITOR_WATCHER_PATTERN = Collections.singleton("cluster:monitor/xpack/watcher/*");
private static final Set<String> MONITOR_ROLLUP_PATTERN = Collections.singleton("cluster:monitor/xpack/rollup/*"); private static final Set<String> MONITOR_ROLLUP_PATTERN = Collections.singleton("cluster:monitor/xpack/rollup/*");
private static final Set<String> ALL_CLUSTER_PATTERN = Collections.unmodifiableSet( private static final Set<String> ALL_CLUSTER_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:*", "indices:admin/template/*", "indices:admin/data_stream/*")); Sets.newHashSet("cluster:*", "indices:admin/template/*", "indices:admin/index_template/*", "indices:admin/data_stream/*"));
private static final Set<String> MANAGE_ML_PATTERN = Collections.unmodifiableSet( private static final Set<String> MANAGE_ML_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/xpack/ml/*", "cluster:monitor/xpack/ml/*")); Sets.newHashSet("cluster:admin/xpack/ml/*", "cluster:monitor/xpack/ml/*"));
private static final Set<String> MANAGE_TRANSFORM_PATTERN = Collections.unmodifiableSet( private static final Set<String> MANAGE_TRANSFORM_PATTERN = Collections.unmodifiableSet(
@ -68,7 +68,8 @@ public class ClusterPrivilegeResolver {
Sets.newHashSet("cluster:admin/xpack/watcher/*", "cluster:monitor/xpack/watcher/*")); Sets.newHashSet("cluster:admin/xpack/watcher/*", "cluster:monitor/xpack/watcher/*"));
private static final Set<String> TRANSPORT_CLIENT_PATTERN = Collections.unmodifiableSet( private static final Set<String> TRANSPORT_CLIENT_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:monitor/nodes/liveness", "cluster:monitor/state")); Sets.newHashSet("cluster:monitor/nodes/liveness", "cluster:monitor/state"));
private static final Set<String> MANAGE_IDX_TEMPLATE_PATTERN = Collections.singleton("indices:admin/template/*"); private static final Set<String> MANAGE_IDX_TEMPLATE_PATTERN = Collections.unmodifiableSet(Sets.newHashSet("indices:admin/template/*",
"indices:admin/index_template/*"));
private static final Set<String> MANAGE_INGEST_PIPELINE_PATTERN = Collections.singleton("cluster:admin/ingest/pipeline/*"); private static final Set<String> MANAGE_INGEST_PIPELINE_PATTERN = Collections.singleton("cluster:admin/ingest/pipeline/*");
private static final Set<String> MANAGE_ROLLUP_PATTERN = Collections.unmodifiableSet( private static final Set<String> MANAGE_ROLLUP_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/xpack/rollup/*", "cluster:monitor/xpack/rollup/*")); Sets.newHashSet("cluster:admin/xpack/rollup/*", "cluster:monitor/xpack/rollup/*"));
@ -212,7 +213,8 @@ public class ClusterPrivilegeResolver {
return actionName.startsWith("cluster:") || return actionName.startsWith("cluster:") ||
actionName.startsWith("indices:admin/template/") || actionName.startsWith("indices:admin/template/") ||
// todo: hack until we implement security of data_streams // todo: hack until we implement security of data_streams
actionName.startsWith("indices:admin/data_stream/"); actionName.startsWith("indices:admin/data_stream/") ||
actionName.startsWith("indices:admin/index_template/");
} }
private static String actionToPattern(String text) { private static String actionToPattern(String text) {