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

Loading external fields.yml files #11199

Closed
Closed
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.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Add `community_id` processor for computing network flow hashes. {pull}10745[10745]
- Add output test to kafka output {pull}10834[10834]
- Add ip fields to default_field in Elasticsearch template. {pull}11035[11035]
- Loading external fields.yml files. {pull}11199[11199]


*Auditbeat*
Expand Down
4 changes: 4 additions & 0 deletions auditbeat/auditbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,10 @@ output.elasticsearch:
#- name: field_name
# type: field_type

# List of external fields.yml files to load.
#setup.template.external_fields:
#- /path/to/external/fields.yml

# Enable JSON template loading. If this is enabled, the fields.yml is ignored.
#setup.template.json.enabled: false

Expand Down
4 changes: 4 additions & 0 deletions filebeat/filebeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,10 @@ output.elasticsearch:
#- name: field_name
# type: field_type

# List of external fields.yml files to load.
#setup.template.external_fields:
#- /path/to/external/fields.yml

# Enable JSON template loading. If this is enabled, the fields.yml is ignored.
#setup.template.json.enabled: false

Expand Down
4 changes: 4 additions & 0 deletions heartbeat/heartbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,10 @@ output.elasticsearch:
#- name: field_name
# type: field_type

# List of external fields.yml files to load.
#setup.template.external_fields:
#- /path/to/external/fields.yml

# Enable JSON template loading. If this is enabled, the fields.yml is ignored.
#setup.template.json.enabled: false

Expand Down
4 changes: 4 additions & 0 deletions journalbeat/journalbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,10 @@ output.elasticsearch:
#- name: field_name
# type: field_type

# List of external fields.yml files to load.
#setup.template.external_fields:
#- /path/to/external/fields.yml

# Enable JSON template loading. If this is enabled, the fields.yml is ignored.
#setup.template.json.enabled: false

Expand Down
4 changes: 4 additions & 0 deletions libbeat/_meta/config.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,10 @@ output.elasticsearch:
#- name: field_name
# type: field_type

# List of external fields.yml files to load.
#setup.template.external_fields:
#- /path/to/external/fields.yml
Copy link

Choose a reason for hiding this comment

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

To me the name kind of clashes with setup.template.fields. Like: which one has priority, does one replace the other, are the two files combined, why not have setup.template.fields accept a list of files.


# Enable JSON template loading. If this is enabled, the fields.yml is ignored.
#setup.template.json.enabled: false

Expand Down
2 changes: 0 additions & 2 deletions libbeat/beat/beat.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ type Beat struct {

BeatConfig *common.Config // The beat's own configuration section

Fields []byte // Data from fields.yml

ConfigManager management.ConfigManager // config manager
}

Expand Down
6 changes: 5 additions & 1 deletion libbeat/cmd/export/index_pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ func GenIndexPatternConfigCmd(settings instance.Settings) *cobra.Command {
if err != nil {
fatalf("Error creating version: %+v", err)
}
indexPattern, err := kibana.NewGenerator(b.Info.IndexPrefix, b.Info.Beat, b.Fields, settings.Version, *v, withMigration)
fields, err := b.Mapping.GetBytes()
if err != nil {
fatalf("Error reading fields of Beat: %+v", err)
}
indexPattern, err := kibana.NewGenerator(b.Info.IndexPrefix, b.Info.Beat, fields, settings.Version, *v, withMigration)
if err != nil {
log.Fatal(err)
}
Expand Down
6 changes: 5 additions & 1 deletion libbeat/cmd/export/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ func GenTemplateConfigCmd(settings instance.Settings) *cobra.Command {
fieldsPath := paths.Resolve(paths.Config, tmplCfg.Fields)
templateString, err = tmpl.LoadFile(fieldsPath)
} else {
templateString, err = tmpl.LoadBytes(b.Fields)
fields, err := b.Mapping.GetBytes()
if err != nil {
fatalf("Error getting fields of Beat: %+v", err)
}
templateString, err = tmpl.LoadBytes(fields)
}
if err != nil {
fatalf("Error generating template: %+v", err)
Expand Down
34 changes: 23 additions & 11 deletions libbeat/cmd/instance/beat.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ import (
"time"

"github.com/elastic/beats/libbeat/kibana"
"github.com/elastic/beats/libbeat/mapping"

"github.com/gofrs/uuid"
errw "github.com/pkg/errors"
"go.uber.org/zap"

"github.com/elastic/beats/libbeat/api"
"github.com/elastic/beats/libbeat/asset"
"github.com/elastic/beats/libbeat/beat"
"github.com/elastic/beats/libbeat/cfgfile"
"github.com/elastic/beats/libbeat/cloudid"
Expand Down Expand Up @@ -77,6 +77,8 @@ type Beat struct {
Config beatConfig
RawConfig *common.Config // Raw config that can be unpacked to get Beat specific config data.

Mapping mapping.Supporter // Get all fields of the Beat
Copy link

Choose a reason for hiding this comment

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

I so wish I hadn't named it 'Supporter' :)


keystore keystore.Keystore
index idxmgmt.Supporter

Expand Down Expand Up @@ -193,11 +195,6 @@ func NewBeat(name, indexPrefix, v string) (*Beat, error) {
return nil, err
}

fields, err := asset.GetFields(name)
if err != nil {
return nil, err
}

id, err := uuid.NewV4()
if err != nil {
return nil, err
Expand All @@ -213,7 +210,6 @@ func NewBeat(name, indexPrefix, v string) (*Beat, error) {
ID: id,
EphemeralID: ephemeralID,
},
Fields: fields,
}

return &Beat{Beat: b}, nil
Expand Down Expand Up @@ -457,8 +453,11 @@ func (b *Beat) Setup(settings Settings, bt beat.Creator, setup SetupSettings) er
}

// prepare index by loading templates, lifecycle policies and write aliases

m := b.index.Manager(esClient, idxmgmt.BeatsAssets(b.Fields))
fields, err := b.Mapping.GetBytes()
if err != nil {
return err
}
m := b.index.Manager(esClient, idxmgmt.BeatsAssets(fields))
Copy link

Choose a reason for hiding this comment

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

The 'BeatsAssets' has been a place-holder, due to not having some better options availble. I'd be in favor of passing a more rich interface instead of a raw blob to be parsed and interpreted somewhere else.

If possible we want to be able to install multiple templates in the future, each with it's own set of fields. A more generic type better being integrated with the index manager would be helpful.

The index manager is supposed to be the only component/interface Beats should use for configuring and preparing indices (facade pattern). Maybe we can move the fields/templating support inside the index manager?

err = m.Setup(setup.Template, setup.ILMPolicy)
if err != nil {
return err
Expand Down Expand Up @@ -606,7 +605,11 @@ func (b *Beat) configure(settings Settings) error {
processingFactory = processing.MakeDefaultBeatSupport(true)
}
b.processing, err = processingFactory(b.Info, logp.L().Named("processors"), b.RawConfig)
if err != nil {
return err
}

b.Mapping, err = mapping.DefaultSupport(nil, b.Beat.Info.Beat, b.RawConfig)
return err
}

Expand Down Expand Up @@ -718,7 +721,11 @@ func (b *Beat) loadDashboards(ctx context.Context, force bool) error {
// but it's assumed that KB and ES have the same minor version.
v := client.GetVersion()

indexPattern, err := kibana.NewGenerator(b.Info.IndexPrefix, b.Info.Beat, b.Fields, b.Info.Version, v, withMigration)
fieldBytes, err := b.Mapping.GetBytes()
if err != nil {
return err
}
indexPattern, err := kibana.NewGenerator(b.Info.IndexPrefix, b.Info.Beat, fieldBytes, b.Info.Version, v, withMigration)
if err != nil {
return fmt.Errorf("error creating index pattern generator: %v", err)
}
Expand Down Expand Up @@ -757,7 +764,12 @@ func (b *Beat) registerESIndexManagement() error {
// Build and return a callback to load index template into ES
func (b *Beat) indexSetupCallback() func(esClient *elasticsearch.Client) error {
return func(esClient *elasticsearch.Client) error {
m := b.index.Manager(esClient, idxmgmt.BeatsAssets(b.Fields))
fieldBytes, err := b.Mapping.GetBytes()
if err != nil {
return err
}

m := b.index.Manager(esClient, idxmgmt.BeatsAssets(fieldBytes))
return m.Setup(true, true)
}
}
Expand Down
146 changes: 146 additions & 0 deletions libbeat/mapping/support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// 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 mapping

import (
"io/ioutil"

"github.com/elastic/beats/libbeat/asset"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
)

// Supporter returns the configured fields bytes
type Supporter interface {
GetBytes() ([]byte, error)
}

// fieldsSupport returns the default fields of a Beat
type fieldsSupport struct {
log *logp.Logger
beat string
}

// customFieldsSupport returns the custom configured fields of a Beat
type customFieldsSupport struct {
log *logp.Logger
beat string
path string
}

// appendFieldsSupport return the default fields of a Beat
// and the additional fields from append_fields
type appendFieldsSupport struct {
original Supporter
appendedFields []byte
}

// externalFieldsSupport return the default fields of a Beat
// and the additional fields from external_fields
type externalFieldsSupport struct {
original Supporter
paths []string
}

func DefaultSupport(log *logp.Logger, beat string, cfg *common.Config) (Supporter, error) {

Choose a reason for hiding this comment

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

exported function DefaultSupport should have comment or be unexported

if log == nil {
log = logp.NewLogger("mapping")
} else {
log = log.Named("mapping")
}

config := struct {
Template struct {
Fields string `config:"fields"`
AppendFields []byte `config:"append_fields"`
ExternalFields []string `config:"external_fields"`
} `config:"setup.template"`
}{}
err := cfg.Unpack(&config)
if err != nil {
return nil, err
}

// load default fields.ymls of Beat
var s Supporter
s = &fieldsSupport{
log: log,
beat: beat,
}

// custom fields.yml file is configured
if config.Template.Fields != "" {
s = &customFieldsSupport{
log: log,
beat: beat,
path: config.Template.Fields,
}
}

// append_fields
if len(config.Template.AppendFields) > 0 {
s = &appendFieldsSupport{
original: s,
appendedFields: config.Template.AppendFields,
}
}

// external_fields
if len(config.Template.ExternalFields) > 0 {
s = &externalFieldsSupport{
original: s,
paths: config.Template.ExternalFields,
}
}

return s, nil
}

func (f *fieldsSupport) GetBytes() ([]byte, error) {
return asset.GetFields(f.beat)
}

func (c *customFieldsSupport) GetBytes() ([]byte, error) {
c.log.Debugf("Reading bytes custom fields.yml from %s", c.path)
return ioutil.ReadFile(c.path)
}

func (a *appendFieldsSupport) GetBytes() ([]byte, error) {
fields, err := a.original.GetBytes()
if err != nil {
return nil, err
}

return append(fields, a.appendedFields...), nil
}

func (e *externalFieldsSupport) GetBytes() ([]byte, error) {
fields, err := e.original.GetBytes()
if err != nil {
return nil, err
}
for _, path := range e.paths {
f, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
fields = append(fields, f...)
}

return fields, nil
}
7 changes: 4 additions & 3 deletions libbeat/template/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ type TemplateConfig struct {
Path string `config:"path"`
Name string `config:"name"`
} `config:"json"`
AppendFields mapping.Fields `config:"append_fields"`
Overwrite bool `config:"overwrite"`
Settings TemplateSettings `config:"settings"`
AppendFields mapping.Fields `config:"append_fields"`
ExternalFields []string `config:"external_fields"`
Overwrite bool `config:"overwrite"`
Settings TemplateSettings `config:"settings"`
}

type TemplateSettings struct {
Expand Down
12 changes: 1 addition & 11 deletions libbeat/template/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,8 @@ func (l *Loader) Load() error {
if err != nil {
return fmt.Errorf("could not unmarshal json template: %s", err)
}
// Load fields from path
} else if l.config.Fields != "" {
logp.Debug("template", "Load fields.yml from file: %s", l.config.Fields)

fieldsPath := paths.Resolve(paths.Config, l.config.Fields)

template, err = tmpl.LoadFile(fieldsPath)
if err != nil {
return fmt.Errorf("error creating template from file %s: %v", fieldsPath, err)
}
} else {
logp.Debug("template", "Load default fields.yml")
logp.Debug("template", "Load fields from configured fields.yml and additions")
template, err = tmpl.LoadBytes(l.fields)
if err != nil {
return fmt.Errorf("error creating template: %v", err)
Expand Down
10 changes: 0 additions & 10 deletions libbeat/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (

"github.com/elastic/beats/libbeat/beat"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/common/cfgwarn"
"github.com/elastic/beats/libbeat/common/fmtstr"
"github.com/elastic/beats/libbeat/mapping"
"github.com/elastic/go-ucfg/yaml"
Expand Down Expand Up @@ -139,15 +138,6 @@ func (t *Template) load(fields mapping.Fields) (common.MapStr, error) {
dynamicTemplates = nil
defaultFields = nil

var err error
if len(t.config.AppendFields) > 0 {
cfgwarn.Experimental("append_fields is used.")
fields, err = mapping.ConcatFields(fields, t.config.AppendFields)
if err != nil {
return nil, err
}
}

// Start processing at the root
properties := common.MapStr{}
processor := Processor{EsVersion: t.esVersion, Migration: t.migration}
Expand Down
Loading