Skip to content

Commit

Permalink
Fix setup.dashboards.index not working (elastic#17749)
Browse files Browse the repository at this point in the history
Due to type casting nightmare, the setting of `setup.dashboards.index`
configuration option to replace the index name in use for dashboards and
index pattern wasn't being honored.

Fixes elastic#14019
  • Loading branch information
adriansr authored Apr 23, 2020
1 parent 61fe9fc commit 07d0c8c
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Fix building on FreeBSD by removing build flags from `add_cloudfoundry_metadata` processor. {pull}17486[17486]
- Do not rotate log files on startup when interval is configured and rotateonstartup is disabled. {pull}17613[17613]
- Fix goroutine leak and Elasticsearch output file descriptor leak when output reloading is in use. {issue}10491[10491] {pull}17381[17381]
- Fix `setup.dashboards.index` setting not working. {pull}17749[17749]

*Auditbeat*

Expand Down
14 changes: 12 additions & 2 deletions libbeat/dashboards/kibana_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import (
"net/url"
"time"

"github.com/joeshaw/multierror"
"github.com/pkg/errors"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/libbeat/kibana"
"github.com/elastic/beats/v7/libbeat/logp"
Expand Down Expand Up @@ -101,11 +104,18 @@ func (loader KibanaLoader) ImportIndexFile(file string) error {

// ImportIndex imports the passed index pattern to Kibana
func (loader KibanaLoader) ImportIndex(pattern common.MapStr) error {
var errs multierror.Errors

params := url.Values{}
params.Set("force", "true") //overwrite the existing dashboards

indexContent := ReplaceIndexInIndexPattern(loader.config.Index, pattern)
return loader.client.ImportJSON(importAPI, params, indexContent)
if err := ReplaceIndexInIndexPattern(loader.config.Index, pattern); err != nil {
errs = append(errs, errors.Wrapf(err, "error setting index '%s' in index pattern", loader.config.Index))
}
if err := loader.client.ImportJSON(importAPI, params, pattern); err != nil {
errs = append(errs, errors.Wrap(err, "error loading index pattern"))
}
return errs.Err()
}

// ImportDashboard imports the dashboard file
Expand Down
52 changes: 36 additions & 16 deletions libbeat/dashboards/modify_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"encoding/json"
"fmt"

"github.com/pkg/errors"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/libbeat/logp"
)
Expand All @@ -41,32 +43,50 @@ type JSONFormat struct {
Objects []JSONObject `json:"objects"`
}

func ReplaceIndexInIndexPattern(index string, content common.MapStr) common.MapStr {
func ReplaceIndexInIndexPattern(index string, content common.MapStr) (err error) {
if index == "" {
return content
return nil
}

objects, ok := content["objects"].([]interface{})
list, ok := content["objects"]
if !ok {
return content
return errors.New("empty index pattern")
}

// change index pattern name
for i, object := range objects {
objectMap, ok := object.(map[string]interface{})
if !ok {
continue
updateObject := func(obj common.MapStr) {
// This uses Put instead of DeepUpdate to avoid modifying types for
// inner objects. (DeepUpdate will replace maps with MapStr).
obj.Put("id", index)
// Only overwrite title if it exists.
if _, err := obj.GetValue("attributes.title"); err == nil {
obj.Put("attributes.title", index)
}
}

objectMap["id"] = index
if attributes, ok := objectMap["attributes"].(map[string]interface{}); ok {
attributes["title"] = index
switch v := list.(type) {
case []interface{}:
for _, objIf := range v {
switch obj := objIf.(type) {
case common.MapStr:
updateObject(obj)
case map[string]interface{}:
updateObject(obj)
default:
return errors.Errorf("index pattern object has unexpected type %T", v)
}
}
objects[i] = objectMap
case []map[string]interface{}:
for _, obj := range v {
updateObject(obj)
}
case []common.MapStr:
for _, obj := range v {
updateObject(obj)
}
default:
return errors.Errorf("index pattern objects have unexpected type %T", v)
}
content["objects"] = objects

return content
return nil
}

func replaceIndexInSearchObject(index string, savedObject string) (string, error) {
Expand Down
134 changes: 134 additions & 0 deletions libbeat/dashboards/modify_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,137 @@ func TestReplaceIndexInDashboardObject(t *testing.T) {
assert.Equal(t, test.expected, result)
}
}

func TestReplaceIndexInIndexPattern(t *testing.T) {
// Test that replacing of index name in index pattern works no matter
// what the inner types are (MapStr, map[string]interface{} or interface{}).
// Also ensures that the inner types are not modified after replacement.
tests := []struct {
title string
input common.MapStr
index string
expected common.MapStr
}{
{
title: "Replace in []interface(map).map",
input: common.MapStr{"objects": []interface{}{map[string]interface{}{
"id": "phonybeat-*",
"type": "index-pattern",
"attributes": map[string]interface{}{
"title": "phonybeat-*",
"timeFieldName": "@timestamp",
}}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []interface{}{map[string]interface{}{
"id": "otherindex-*",
"type": "index-pattern",
"attributes": map[string]interface{}{
"title": "otherindex-*",
"timeFieldName": "@timestamp",
}}}},
},
{
title: "Replace in []interface(map).mapstr",
input: common.MapStr{"objects": []interface{}{map[string]interface{}{
"id": "phonybeat-*",
"type": "index-pattern",
"attributes": common.MapStr{
"title": "phonybeat-*",
"timeFieldName": "@timestamp",
}}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []interface{}{map[string]interface{}{
"id": "otherindex-*",
"type": "index-pattern",
"attributes": common.MapStr{
"title": "otherindex-*",
"timeFieldName": "@timestamp",
}}}},
},
{
title: "Replace in []map.mapstr",
input: common.MapStr{"objects": []map[string]interface{}{{
"id": "phonybeat-*",
"type": "index-pattern",
"attributes": common.MapStr{
"title": "phonybeat-*",
"timeFieldName": "@timestamp",
}}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []map[string]interface{}{{
"id": "otherindex-*",
"type": "index-pattern",
"attributes": common.MapStr{
"title": "otherindex-*",
"timeFieldName": "@timestamp",
}}}},
},
{
title: "Replace in []mapstr.mapstr",
input: common.MapStr{"objects": []common.MapStr{{
"id": "phonybeat-*",
"type": "index-pattern",
"attributes": common.MapStr{
"title": "phonybeat-*",
"timeFieldName": "@timestamp",
}}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []common.MapStr{{
"id": "otherindex-*",
"type": "index-pattern",
"attributes": common.MapStr{
"title": "otherindex-*",
"timeFieldName": "@timestamp",
}}}},
},
{
title: "Replace in []mapstr.interface(mapstr)",
input: common.MapStr{"objects": []common.MapStr{{
"id": "phonybeat-*",
"type": "index-pattern",
"attributes": interface{}(common.MapStr{
"title": "phonybeat-*",
"timeFieldName": "@timestamp",
})}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []common.MapStr{{
"id": "otherindex-*",
"type": "index-pattern",
"attributes": interface{}(common.MapStr{
"title": "otherindex-*",
"timeFieldName": "@timestamp",
})}}},
},
{
title: "Do not create missing attributes",
input: common.MapStr{"objects": []common.MapStr{{
"id": "phonybeat-*",
"type": "index-pattern",
}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []common.MapStr{{
"id": "otherindex-*",
"type": "index-pattern",
}}},
},
{
title: "Create missing id",
input: common.MapStr{"objects": []common.MapStr{{
"type": "index-pattern",
}}},
index: "otherindex-*",
expected: common.MapStr{"objects": []common.MapStr{{
"id": "otherindex-*",
"type": "index-pattern",
}}},
},
}

for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
err := ReplaceIndexInIndexPattern(test.index, test.input)
assert.NoError(t, err)
assert.Equal(t, test.expected, test.input)
})
}
}

0 comments on commit 07d0c8c

Please sign in to comment.