Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configuration to allow users to retry loading dashboards #6560

Merged
merged 5 commits into from
Mar 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ https://github.com/elastic/beats/compare/v6.0.0-beta2...master[Check the HEAD di
- Experimental feature setup.template.append_fields added. {pull}6024[6024]
- Add appender support to autodiscover {pull}6469[6469]
- Add add_host_metadata processor {pull}5968[5968]
- Retry configuration to load dashboards if Kibana is not reachable when the beat starts. {pull}6560[6560]

*Auditbeat*

Expand Down
11 changes: 11 additions & 0 deletions auditbeat/auditbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down
11 changes: 11 additions & 0 deletions filebeat/filebeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down
11 changes: 11 additions & 0 deletions heartbeat/heartbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down
11 changes: 11 additions & 0 deletions libbeat/_meta/config.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down
13 changes: 8 additions & 5 deletions libbeat/cmd/instance/beat.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package instance

import (
"context"
cryptRand "crypto/rand"
"encoding/json"
"flag"
Expand Down Expand Up @@ -294,9 +295,10 @@ func (b *Beat) launch(bt beat.Creator) error {
return beat.GracefulExit
}

svc.HandleSignals(beater.Stop)
ctx, cancel := context.WithCancel(context.Background())
svc.HandleSignals(beater.Stop, cancel)

err = b.loadDashboards(false)
err = b.loadDashboards(ctx, false)
if err != nil {
return err
}
Expand Down Expand Up @@ -382,7 +384,8 @@ func (b *Beat) Setup(bt beat.Creator, template, dashboards, machineLearning bool
}

if dashboards {
err = b.loadDashboards(true)
fmt.Println("Loading dashboards (Kibana must be running and reachable)")
err = b.loadDashboards(context.Background(), true)
if err != nil {
return err
}
Expand Down Expand Up @@ -565,7 +568,7 @@ func openRegular(filename string) (*os.File, error) {
return f, nil
}

func (b *Beat) loadDashboards(force bool) error {
func (b *Beat) loadDashboards(ctx context.Context, force bool) error {
if setup || force {
// -setup implies dashboards.enabled=true
if b.Config.Dashboards == nil {
Expand All @@ -583,7 +586,7 @@ func (b *Beat) loadDashboards(force bool) error {
if b.Config.Output.Name() == "elasticsearch" {
esConfig = b.Config.Output.Config()
}
err := dashboards.ImportDashboards(b.Info.Beat, b.Info.Hostname, paths.Resolve(paths.Home, ""),
err := dashboards.ImportDashboards(ctx, b.Info.Beat, b.Info.Hostname, paths.Resolve(paths.Home, ""),
b.Config.Kibana, esConfig, b.Config.Dashboards, nil)
if err != nil {
return fmt.Errorf("Error importing Kibana dashboards: %v", err)
Expand Down
14 changes: 14 additions & 0 deletions libbeat/dashboards/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package dashboards

import "time"

type Config struct {
Enabled bool `config:"enabled"`
KibanaIndex string `config:"kibana_index"`
Expand All @@ -11,11 +13,23 @@ type Config struct {
OnlyDashboards bool `config:"only_dashboards"`
OnlyIndex bool `config:"only_index"`
AlwaysKibana bool `config:"always_kibana"`
Retry *Retry `config:"retry"`
}

type Retry struct {
Enabled bool `config:"enabled"`
Interval time.Duration `config:"interval"`
Maximum uint `config:"maximum"`
}

var defaultConfig = Config{
KibanaIndex: ".kibana",
AlwaysKibana: false,
Retry: &Retry{
Enabled: false,
Interval: time.Second,
Maximum: 0,
},
}
var (
defaultDirectory = "kibana"
Expand Down
8 changes: 5 additions & 3 deletions libbeat/dashboards/dashboards.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dashboards

import (
"context"
"errors"
"fmt"
"path/filepath"
Expand All @@ -24,6 +25,7 @@ const (
// 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,
Expand Down Expand Up @@ -102,16 +104,16 @@ func ImportDashboards(
case importViaES:
return ImportDashboardsViaElasticsearch(esLoader)
case importViaKibana:
return setupAndImportDashboardsViaKibana(hostname, kibanaConfig, &dashConfig, msgOutputter)
return setupAndImportDashboardsViaKibana(ctx, hostname, kibanaConfig, &dashConfig, msgOutputter)
default:
return errors.New("Elasticsearch or Kibana configuration missing for loading dashboards.")
}
}

func setupAndImportDashboardsViaKibana(hostname string, kibanaConfig *common.Config,
func setupAndImportDashboardsViaKibana(ctx context.Context, hostname string, kibanaConfig *common.Config,
dashboardsConfig *Config, msgOutputter MessageOutputter) error {

kibanaLoader, err := NewKibanaLoader(kibanaConfig, dashboardsConfig, hostname, msgOutputter)
kibanaLoader, err := NewKibanaLoader(ctx, kibanaConfig, dashboardsConfig, hostname, msgOutputter)
if err != nil {
return fmt.Errorf("fail to create the Kibana loader: %v", err)
}
Expand Down
22 changes: 20 additions & 2 deletions libbeat/dashboards/kibana_loader.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package dashboards

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"time"

"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
Expand All @@ -21,13 +23,13 @@ type KibanaLoader struct {
msgOutputter MessageOutputter
}

func NewKibanaLoader(cfg *common.Config, dashboardsConfig *Config, hostname string, msgOutputter MessageOutputter) (*KibanaLoader, error) {
func NewKibanaLoader(ctx context.Context, cfg *common.Config, dashboardsConfig *Config, hostname string, msgOutputter MessageOutputter) (*KibanaLoader, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewKibanaLoader should have comment or be unexported


if cfg == nil || !cfg.Enabled() {
return nil, fmt.Errorf("Kibana is not configured or enabled")
}

client, err := kibana.NewKibanaClient(cfg)
client, err := getKibanaClient(ctx, cfg, dashboardsConfig.Retry, 0)
if err != nil {
return nil, fmt.Errorf("Error creating Kibana client: %v", err)
}
Expand All @@ -45,6 +47,22 @@ func NewKibanaLoader(cfg *common.Config, dashboardsConfig *Config, hostname stri
return &loader, nil
}

func getKibanaClient(ctx context.Context, cfg *common.Config, retryCfg *Retry, retryAttempt uint) (*kibana.Client, error) {
client, err := kibana.NewKibanaClient(cfg)
if err != nil {
if retryCfg.Enabled && (retryCfg.Maximum == 0 || retryCfg.Maximum > retryAttempt) {
select {
case <-ctx.Done():
return nil, err
case <-time.After(retryCfg.Interval):
return getKibanaClient(ctx, cfg, retryCfg, retryAttempt+1)
}
}
return nil, fmt.Errorf("Error creating Kibana client: %v", err)
}
return client, nil
}

func (loader KibanaLoader) ImportIndex(file string) error {
params := url.Values{}
params.Set("force", "true") //overwrite the existing dashboards
Expand Down
17 changes: 17 additions & 0 deletions libbeat/docs/dashboardsconfig.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,20 @@ NOTE: This setting only works for Kibana 6.0 and newer.

Force loading of dashboards using the Kibana API without querying Elasticsearch for the version
The default is `false`.

[float]
==== `setup.dashboards.retry.enabled`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the docs.


If this option is set to true, and Kibana is not reachable at the time when dashboards are loaded,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment above made me realise that having a comment about the defaults here would also be great. Some at then of the sentence like. Default is false.

{beatname_uc} will retry to reconnect to Kibana instead of exiting with an error. Disabled by default.

[float]
==== `setup.dashboards.retry.interval`

Duration interval between Kibana connection retries. Defaults to 1 second.

[float]
==== `setup.dashboards.retry.maximum`

Maximum number of retries before exiting with an error. Set to 0 for unlimited retrying.
Default is unlimited.
4 changes: 3 additions & 1 deletion libbeat/service/service.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package service

import (
"context"
"expvar"
"flag"
"fmt"
Expand All @@ -22,7 +23,7 @@ import (
// HandleSignals manages OS signals that ask the service/daemon to stop.
// The stopFunction should break the loop in the Beat so that
// the service shut downs gracefully.
func HandleSignals(stopFunction func()) {
func HandleSignals(stopFunction func(), cancel context.CancelFunc) {
var callback sync.Once

// On ^C or SIGTERM, gracefully stop the sniffer
Expand All @@ -31,6 +32,7 @@ func HandleSignals(stopFunction func()) {
go func() {
<-sigc
logp.Debug("service", "Received sigterm/sigint, stopping")
cancel()
callback.Do(stopFunction)
}()

Expand Down
11 changes: 11 additions & 0 deletions metricbeat/metricbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down
11 changes: 11 additions & 0 deletions packetbeat/packetbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down
11 changes: 11 additions & 0 deletions winlogbeat/winlogbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,17 @@ output.elasticsearch:
# how to install the dashboards by first querying Elasticsearch.
#setup.dashboards.always_kibana: false

# If true and Kibana is not reachable at the time when dashboards are loaded,
# it will retry to reconnect to Kibana instead of exiting with an error.
#setup.dashboards.retry.enabled: false

# Duration interval between Kibana connection retries.
#setup.dashboards.retry.interval: 1s

# Maximum number of retries before exiting with an error, 0 for unlimited retrying.
#setup.dashboards.retry.maximum: 0


#============================== Template =====================================

# A template is used to set the mapping in Elasticsearch
Expand Down