/*
* Licensed to Crate under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership. Crate 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial
* agreement.
*/
package io.crate.metadata;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import io.crate.Constants;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import java.io.IOException;
/**
* Service that ensures a "crate_defaults" template exists.
*
* This template matches all indices and customizes dynamic mapping updates:
*
* - dynamically added strings use docValues
*/
public class DefaultTemplateService extends AbstractComponent {
final static String TEMPLATE_NAME = "crate_defaults";
private final static String DEFAULT_MAPPING_SOURCE = createDefaultMappingSource();
private final ClusterService clusterService;
public DefaultTemplateService(Settings settings, ClusterService clusterService) {
super(settings);
this.clusterService = clusterService;
}
public void createIfNotExists(ClusterState state) {
DiscoveryNodes nodes = state.nodes();
if (nodes.getMasterNodeId().equals(nodes.getLocalNodeId()) == false) {
return;
}
if (state.getMetaData().getTemplates().containsKey(TEMPLATE_NAME) == false) {
createDefaultTemplate();
}
}
private void createDefaultTemplate() {
clusterService.submitStateUpdateTask("ensure-default-template", new ClusterStateUpdateTask() {
@Override
public ClusterState execute(ClusterState currentState) throws Exception {
return addDefaultTemplate(currentState);
}
@Override
public void onFailure(String source, Exception e) {
logger.error("Error during ensure-default-template source={}", e, source);
}
});
}
@VisibleForTesting
static ClusterState addDefaultTemplate(ClusterState currentState) throws IOException {
MetaData currentMetaData = currentState.getMetaData();
ImmutableOpenMap<String, IndexTemplateMetaData> currentTemplates = currentMetaData.getTemplates();
ImmutableOpenMap<String, IndexTemplateMetaData> newTemplates = createCopyWithDefaultTemplateAdded(currentTemplates);
MetaData.Builder mdBuilder = MetaData.builder(currentMetaData).templates(newTemplates);
return ClusterState.builder(currentState).metaData(mdBuilder).build();
}
private static ImmutableOpenMap<String, IndexTemplateMetaData> createCopyWithDefaultTemplateAdded(
ImmutableOpenMap<String, IndexTemplateMetaData> currentTemplates) throws IOException {
IndexTemplateMetaData defaultTemplate = IndexTemplateMetaData.builder(TEMPLATE_NAME)
.order(0)
.putMapping(Constants.DEFAULT_MAPPING_TYPE, DEFAULT_MAPPING_SOURCE)
.template("*")
.build();
ImmutableOpenMap.Builder<String, IndexTemplateMetaData> builder = ImmutableOpenMap.builder(currentTemplates);
builder.put(TEMPLATE_NAME, defaultTemplate);
return builder.build();
}
private static String createDefaultMappingSource() {
try {
// @formatter:off
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.startObject(Constants.DEFAULT_MAPPING_TYPE)
.startArray("dynamic_templates")
.startObject()
.startObject("strings")
.field("match_mapping_type", "string")
.startObject("mapping")
.field("type", "keyword")
.field("doc_values", true)
.field("store", false)
.endObject()
.endObject()
.endObject()
.endArray()
.endObject()
.endObject();
// @formatter:on
return builder.bytes().utf8ToString();
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
}