io.fabric8.elasticsearch.plugin.kibana.KibanaSeed Maven / Gradle / Ivy
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed 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 io.fabric8.elasticsearch.plugin.kibana;
import static io.fabric8.elasticsearch.plugin.KibanaUserReindexFilter.getUsernameHash;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.search.SearchHit;
public class KibanaSeed {
private static ESLogger logger = Loggers.getLogger(KibanaSeed.class);
private static final String DEFAULT_INDEX_TYPE = "config";
private static final String INDICIES_TYPE = "index-pattern";
private static final String OPERATIONS_PROJECT = ".operations";
private static final String BLANK_PROJECT = ".empty-project";
private static final String ADMIN_ALIAS_NAME = ".all";
//TODO: should these be able to be read from property values?
private static final String[] OPERATIONS_ROLES = {"cluster-admin"};
public static final String DEFAULT_INDEX_FIELD = "defaultIndex";
public static void setDashboards(String user, Set projects, Set roles, Client esClient, String kibanaIndex, String kibanaVersion) {
boolean isAdmin = false;
//GET .../.kibana/index-pattern/_search?pretty=true&fields=
// compare results to projects; handle any deltas (create, delete?)
Set indexPatterns = getIndexPatterns(user, esClient, kibanaIndex);
logger.debug("Found '{}' Index patterns for user", indexPatterns.size());
// Check roles here, if user is a cluster-admin we should add .operations to their project? -- correct way to do this?
logger.debug("Checking for '{}' in users roles '{}'", OPERATIONS_ROLES, roles);
for ( String role : OPERATIONS_ROLES )
if ( roles.contains(role) ) {
logger.debug("{} is an admin user", user);
projects.add(OPERATIONS_PROJECT);
isAdmin = true;
projects.add(ADMIN_ALIAS_NAME);
break;
}
List sortedProjects = new ArrayList(projects);
Collections.sort(sortedProjects);
if ( sortedProjects.isEmpty() )
sortedProjects.add(BLANK_PROJECT);
logger.debug("Setting dashboards given user '{}' and projects '{}'", user, projects);
if ( isAdmin ) {
logger.debug("Adding to alias for {}", user);
buildAdminAlias(user, sortedProjects, esClient, kibanaIndex, kibanaVersion);
}
// If none have been set yet
if ( indexPatterns.isEmpty() ) {
create(user, sortedProjects, true, esClient, kibanaIndex, kibanaVersion);
}
else {
List common = new ArrayList(indexPatterns);
common.retainAll(sortedProjects);
sortedProjects.removeAll(common);
indexPatterns.removeAll(common);
// if we aren't a cluster-admin, make sure we're deleting the ADMIN_ALIAS_NAME
if ( !isAdmin ) {
logger.debug("user is not a cluster admin, ensure they don't keep/have the admin alias pattern");
indexPatterns.add(ADMIN_ALIAS_NAME);
}
// for any to create (remaining in projects) call createIndices, createSearchmapping?, create dashboard
create(user, sortedProjects, false, esClient, kibanaIndex, kibanaVersion);
// cull any that are in ES but not in OS (remaining in indexPatterns)
remove(user, indexPatterns, esClient, kibanaIndex);
common.addAll(sortedProjects);
Collections.sort(common);
// Set default index to first index in common if we removed the default
String defaultIndex = getDefaultIndex(user, esClient, kibanaIndex, kibanaVersion);
logger.debug("Checking if '{}' contains '{}'", indexPatterns, defaultIndex);
if ( indexPatterns.contains(defaultIndex) || StringUtils.isEmpty(defaultIndex) ) {
logger.debug("'{}' does contain '{}' and common size is {}", indexPatterns, defaultIndex, common.size());
if ( common.size() > 0 )
setDefaultIndex(user, common.get(0), esClient, kibanaIndex, kibanaVersion);
}
}
}
// this may return other than void later...
private static void setDefaultIndex(String username, String project, Client esClient, String kibanaIndex, String kibanaVersion) {
// this will create a default index of [index.]YYYY.MM.DD in .kibana.username
String source = new DocumentBuilder()
.defaultIndex(getIndexPattern(project))
.build();
executeCreate(getKibanaIndex(username, kibanaIndex), DEFAULT_INDEX_TYPE, kibanaVersion, source, esClient);
}
private static void buildAdminAlias(String username, List projects, Client esClient, String kibanaIndex, String kibanaVersion) {
List toAdd = new ArrayList(projects);
try {
for ( String project : projects ) {
// Check that the index exists before we try to alias it...
IndicesExistsResponse existsResponse = esClient.admin().indices().prepareExists(getIndexPattern(project)).get();
logger.debug("Checking if index {} exists? {}", project, existsResponse.isExists());
if ( !existsResponse.isExists() || project.equalsIgnoreCase(ADMIN_ALIAS_NAME)) {
toAdd.remove(project);
}
}
if ( toAdd.isEmpty() )
return;
IndicesAliasesRequestBuilder aliasBuilder = esClient.admin().indices().prepareAliases();
for ( String project : toAdd ) {
logger.debug("Creating alias for {} as {}", project, ADMIN_ALIAS_NAME);
aliasBuilder.addAlias(getIndexPattern(project), ADMIN_ALIAS_NAME);
}
IndicesAliasesResponse response = aliasBuilder.get();
logger.debug("Aliases request acknowledged? {}", response.isAcknowledged());
} catch (ElasticsearchException e) {
// Avoid printing out any kibana specific information?
logger.error("Error executing Alias request", e);
}
}
private static String getDefaultIndex(String username, Client esClient, String kibanaIndex, String kibanaVersion) {
GetRequest request = esClient.prepareGet(getKibanaIndex(username, kibanaIndex), DEFAULT_INDEX_TYPE, kibanaVersion).request();
try {
GetResponse response = esClient.get(request).get();
Map source = response.getSource();
if ( source.containsKey(DEFAULT_INDEX_FIELD) ) {
logger.debug("Received response with 'defaultIndex' = {}", source.get(DEFAULT_INDEX_FIELD));
String index = (String) source.get(DEFAULT_INDEX_FIELD);
return getProjectFromIndex(index);
}
else {
logger.debug("Received response without 'defaultIndex'");
}
} catch (InterruptedException | ExecutionException e) {
if ( e.getCause() instanceof org.elasticsearch.indices.IndexMissingException ) {
logger.debug("No index found");
}
else {
logger.error("Error getting default index for {}", e, username);
}
}
return "";
}
private static void create(String user, List projects, boolean setDefault, Client esClient, String kibanaIndex, String kibanaVersion) {
boolean defaultSet = !setDefault;
for ( String project: projects ) {
createIndex(user, project, esClient, kibanaIndex);
//set default
if ( !defaultSet ) {
setDefaultIndex(user, project, esClient, kibanaIndex, kibanaVersion);
defaultSet = true;
}
}
}
private static void remove(String user, Set projects, Client esClient, String kibanaIndex) {
for ( String project: projects ) {
deleteIndex(user, project, esClient, kibanaIndex);
}
}
// This is a mis-nomer... it actually returns the project name of index patterns (.operations included)
private static Set getIndexPatterns(String username, Client esClient, String kibanaIndex) {
Set patterns = new HashSet();
SearchRequest request = esClient.prepareSearch(getKibanaIndex(username, kibanaIndex))
.setTypes(INDICIES_TYPE)
.request();
try {
SearchResponse response = esClient.search(request).get();
if ( response.getHits() != null && response.getHits().getTotalHits() > 0 )
for ( SearchHit hit : response.getHits().getHits() ) {
String id = hit.getId();
String project = getProjectFromIndex(id);
if ( !project.equals(id) )
patterns.add(project);
// else -> this is user created, leave it alone
}
} catch (InterruptedException | ExecutionException e) {
// if is ExecutionException with cause of IndexMissingException
if ( e.getCause() instanceof org.elasticsearch.indices.IndexMissingException ) {
logger.debug("Encountered IndexMissingException, returning empty response");
}
else {
logger.error("Error getting index patterns for {}", e, username);
}
}
return patterns;
}
private static void createIndex(String username, String project, Client esClient, String kibanaIndex) {
DocumentBuilder sourceBuilder = new DocumentBuilder()
.title(getIndexPattern(project))
.timeFieldName("time");
if ( project.equalsIgnoreCase(OPERATIONS_PROJECT) || project.equalsIgnoreCase(ADMIN_ALIAS_NAME) )
sourceBuilder.operationsFields();
else if ( project.equalsIgnoreCase(BLANK_PROJECT) )
sourceBuilder.blankFields();
else
sourceBuilder.applicationFields();
String source = sourceBuilder.build();
executeCreate(getKibanaIndex(username, kibanaIndex), INDICIES_TYPE, getIndexPattern(project), source, esClient);
}
private static void deleteIndex(String username, String project, Client esClient, String kibanaIndex) {
executeDelete(getKibanaIndex(username, kibanaIndex), INDICIES_TYPE, getIndexPattern(project), esClient);
}
private static void executeCreate(String index, String type, String id, String source, Client esClient) {
logger.debug("CREATE: '{}/{}/{}' source: '{}'", index, type, id, source);
IndexRequest request = esClient.prepareIndex(index, type, id).setSource(source).request();
try {
esClient.index(request).get();
} catch (InterruptedException | ExecutionException e) {
// Avoid printing out any kibana specific information?
logger.error("Error executing create request", e);
}
}
private static void executeDelete(String index, String type, String id, Client esClient) {
logger.debug("DELETE: '{}/{}/{}'", index, type, id);
DeleteRequest request = esClient.prepareDelete(index, type, id).request();
try {
esClient.delete(request).get();
} catch (InterruptedException | ExecutionException e) {
// Avoid printing out any kibana specific information?
logger.error("Error executing delete request", e);
}
}
private static String getKibanaIndex(String username, String kibanaIndex) {
return kibanaIndex + "." + getUsernameHash(username);
}
private static String getIndexPattern(String project) {
if ( project.equalsIgnoreCase(ADMIN_ALIAS_NAME) )
return project;
return project + ".*";
}
private static String getProjectFromIndex(String index) {
if ( !StringUtils.isEmpty(index) ) {
if ( index.equalsIgnoreCase(ADMIN_ALIAS_NAME) )
return index;
int wildcard = index.lastIndexOf('.');
if ( wildcard > 0 )
return index.substring(0, wildcard);
}
return index;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy