160 lines
4.9 KiB
Go
160 lines
4.9 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 template
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
|
||
|
"github.com/elastic/beats/libbeat/beat"
|
||
|
"github.com/elastic/beats/libbeat/common"
|
||
|
"github.com/elastic/beats/libbeat/logp"
|
||
|
"github.com/elastic/beats/libbeat/paths"
|
||
|
)
|
||
|
|
||
|
// TemplateLoader is a subset of the Elasticsearch client API capable of
|
||
|
// loading the template.
|
||
|
type ESClient interface {
|
||
|
LoadJSON(path string, json map[string]interface{}) ([]byte, error)
|
||
|
Request(method, path string, pipeline string, params map[string]string, body interface{}) (int, []byte, error)
|
||
|
GetVersion() string
|
||
|
}
|
||
|
|
||
|
type Loader struct {
|
||
|
config TemplateConfig
|
||
|
client ESClient
|
||
|
beatInfo beat.Info
|
||
|
fields []byte
|
||
|
}
|
||
|
|
||
|
// NewLoader creates a new template loader
|
||
|
func NewLoader(cfg *common.Config, client ESClient, beatInfo beat.Info, fields []byte) (*Loader, error) {
|
||
|
config := DefaultConfig
|
||
|
|
||
|
err := cfg.Unpack(&config)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &Loader{
|
||
|
config: config,
|
||
|
client: client,
|
||
|
beatInfo: beatInfo,
|
||
|
fields: fields,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Load checks if the index mapping template should be loaded
|
||
|
// In case the template is not already loaded or overwriting is enabled, the
|
||
|
// template is written to index
|
||
|
func (l *Loader) Load() error {
|
||
|
|
||
|
tmpl, err := New(l.beatInfo.Version, l.beatInfo.IndexPrefix, l.client.GetVersion(), l.config)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error creating template instance: %v", err)
|
||
|
}
|
||
|
|
||
|
templateName := tmpl.GetName()
|
||
|
if l.config.JSON.Enabled {
|
||
|
templateName = l.config.JSON.Name
|
||
|
}
|
||
|
// Check if template already exist or should be overwritten
|
||
|
exists := l.CheckTemplate(templateName)
|
||
|
if !exists || l.config.Overwrite {
|
||
|
|
||
|
logp.Info("Loading template for Elasticsearch version: %s", l.client.GetVersion())
|
||
|
if l.config.Overwrite {
|
||
|
logp.Info("Existing template will be overwritten, as overwrite is enabled.")
|
||
|
}
|
||
|
|
||
|
var template map[string]interface{}
|
||
|
if l.config.JSON.Enabled {
|
||
|
jsonPath := paths.Resolve(paths.Config, l.config.JSON.Path)
|
||
|
if _, err := os.Stat(jsonPath); err != nil {
|
||
|
return fmt.Errorf("error checking for json template: %s", err)
|
||
|
}
|
||
|
|
||
|
logp.Info("Loading json template from file %s", jsonPath)
|
||
|
|
||
|
content, err := ioutil.ReadFile(jsonPath)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error reading file. Path: %s, Error: %s", jsonPath, err)
|
||
|
|
||
|
}
|
||
|
err = json.Unmarshal(content, &template)
|
||
|
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")
|
||
|
template, err = tmpl.LoadBytes(l.fields)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error creating template: %v", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = l.LoadTemplate(templateName, template)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not load template. Elasticsearch returned: %v. Template is: %s", err, template)
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
logp.Info("Template already exists and will not be overwritten.")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// LoadTemplate loads a template into Elasticsearch overwriting the existing
|
||
|
// template if it exists. If you wish to not overwrite an existing template
|
||
|
// then use CheckTemplate prior to calling this method.
|
||
|
func (l *Loader) LoadTemplate(templateName string, template map[string]interface{}) error {
|
||
|
logp.Debug("template", "Try loading template with name: %s", templateName)
|
||
|
path := "/_template/" + templateName
|
||
|
body, err := l.client.LoadJSON(path, template)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("couldn't load template: %v. Response body: %s", err, body)
|
||
|
}
|
||
|
logp.Info("Elasticsearch template with name '%s' loaded", templateName)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// CheckTemplate checks if a given template already exist. It returns true if
|
||
|
// and only if Elasticsearch returns with HTTP status code 200.
|
||
|
func (l *Loader) CheckTemplate(templateName string) bool {
|
||
|
status, _, _ := l.client.Request("HEAD", "/_template/"+templateName, "", nil, nil)
|
||
|
|
||
|
if status != 200 {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|