// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. 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 dashboards import ( "context" "errors" "fmt" "path/filepath" "strconv" "strings" errw "github.com/pkg/errors" "github.com/elastic/beats/libbeat/common" ) type importMethod uint8 // check import route const ( importNone importMethod = iota importViaKibana importViaES ) // ImportDashboards tries to import the kibana dashboards. // If the Elastic Stack is at version 6.0+, the dashboards should be installed // via the kibana dashboard loader plugin. For older versions of the Elastic Stack // we write the dashboards directly into the .kibana index. func ImportDashboards( ctx context.Context, beatName, hostname, homePath string, kibanaConfig, esConfig, dashboardsConfig *common.Config, msgOutputter MessageOutputter, ) error { if dashboardsConfig == nil || !dashboardsConfig.Enabled() { return nil } // unpack dashboard config dashConfig := defaultConfig dashConfig.Beat = beatName dashConfig.Dir = filepath.Join(homePath, defaultDirectory) err := dashboardsConfig.Unpack(&dashConfig) if err != nil { return err } // init kibana config object if kibanaConfig == nil { kibanaConfig = common.NewConfig() } if esConfig.Enabled() { username, _ := esConfig.String("username", -1) password, _ := esConfig.String("password", -1) if !kibanaConfig.HasField("username") && username != "" { kibanaConfig.SetString("username", -1, username) } if !kibanaConfig.HasField("password") && password != "" { kibanaConfig.SetString("password", -1, password) } } var esLoader *ElasticsearchLoader importVia := importNone useKibana := importViaKibana if !kibanaConfig.Enabled() { useKibana = importNone } requiresKibana := dashConfig.AlwaysKibana || !esConfig.Enabled() if requiresKibana { importVia = useKibana } else { // Check import route via elasticsearch version. If Elasticsearch major // version is >6, we assume Kibana also being at versions >6.0. In this // case dashboards will be imported using the new kibana dashboard loader // plugin. // XXX(urso): Why do we test the Elasticsearch version? If kibana is // configured, why not test the kibana version and plugin // availability first? esLoader, err = NewElasticsearchLoader(esConfig, &dashConfig, msgOutputter) if err != nil { return fmt.Errorf("fail to create the Elasticsearch loader: %v", err) } defer esLoader.Close() esLoader.statusMsg("Elasticsearch URL %v", esLoader.client.Connection.URL) majorVersion, _, err := getMajorAndMinorVersion(esLoader.version) if err != nil { return fmt.Errorf("wrong Elasticsearch version: %v", err) } if majorVersion < 6 { importVia = importViaES } else { importVia = useKibana } } // Try to import dashboards. switch importVia { case importViaES: return ImportDashboardsViaElasticsearch(esLoader) case importViaKibana: return setupAndImportDashboardsViaKibana(ctx, hostname, kibanaConfig, &dashConfig, msgOutputter) default: return errors.New("Elasticsearch or Kibana configuration missing for loading dashboards.") } } func setupAndImportDashboardsViaKibana(ctx context.Context, hostname string, kibanaConfig *common.Config, dashboardsConfig *Config, msgOutputter MessageOutputter) error { kibanaLoader, err := NewKibanaLoader(ctx, kibanaConfig, dashboardsConfig, hostname, msgOutputter) if err != nil { return fmt.Errorf("fail to create the Kibana loader: %v", err) } defer kibanaLoader.Close() kibanaLoader.statusMsg("Kibana URL %v", kibanaLoader.client.Connection.URL) return ImportDashboardsViaKibana(kibanaLoader) } func ImportDashboardsViaKibana(kibanaLoader *KibanaLoader) error { if !isKibanaAPIavailable(kibanaLoader.version) { return fmt.Errorf("Kibana API is not available in Kibana version %s", kibanaLoader.version) } version, err := common.NewVersion(kibanaLoader.version) if err != nil { return fmt.Errorf("Invalid Kibana version: %s", kibanaLoader.version) } importer, err := NewImporter(*version, kibanaLoader.config, kibanaLoader) if err != nil { return fmt.Errorf("fail to create a Kibana importer for loading the dashboards: %v", err) } if err := importer.Import(); err != nil { return errw.Wrap(err, "fail to import the dashboards in Kibana") } return nil } func ImportDashboardsViaElasticsearch(esLoader *ElasticsearchLoader) error { if err := esLoader.CreateKibanaIndex(); err != nil { return fmt.Errorf("fail to create the kibana index: %v", err) } version, _ := common.NewVersion("5.0.0") importer, err := NewImporter(*version, esLoader.config, esLoader) if err != nil { return fmt.Errorf("fail to create an Elasticsearch importer for loading the dashboards: %v", err) } if err := importer.Import(); err != nil { return fmt.Errorf("fail to import the dashboards in Elasticsearch: %v", err) } return nil } func getMajorAndMinorVersion(version string) (int, int, error) { fields := strings.Split(version, ".") if len(fields) != 3 { return 0, 0, fmt.Errorf("wrong version %s", version) } majorVersion := fields[0] minorVersion := fields[1] majorVersionInt, err := strconv.Atoi(majorVersion) if err != nil { return 0, 0, err } minorVersionInt, err := strconv.Atoi(minorVersion) if err != nil { return 0, 0, err } return majorVersionInt, minorVersionInt, nil } func isKibanaAPIavailable(version string) bool { majorVersion, minorVersion, err := getMajorAndMinorVersion(version) if err != nil { return false } if majorVersion == 5 && minorVersion >= 6 { return true } if majorVersion >= 6 { return true } return false }