227 lines
6.2 KiB
Go
227 lines
6.2 KiB
Go
|
// 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
|
||
|
}
|