feat: Migrate xpath parser to new style (#11218)

This commit is contained in:
Sven Rebhan 2022-06-08 21:39:02 +02:00 committed by GitHub
parent 0b7c3c4b24
commit 0d96968819
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 261 additions and 181 deletions

View File

@ -1842,9 +1842,7 @@ func (c *Config) missingTomlField(_ reflect.Type, key string) error {
"prefix", "prometheus_export_timestamp", "prometheus_ignore_timestamp", "prometheus_sort_metrics", "prometheus_string_as_label",
"separator", "splunkmetric_hec_routing", "splunkmetric_multimetric", "tag_keys",
"tagdrop", "tagexclude", "taginclude", "tagpass", "tags", "template", "templates",
"value_field_name", "wavefront_source_override", "wavefront_use_strict", "wavefront_disable_prefix_conversion",
"xml", "xpath", "xpath_json", "xpath_msgpack", "xpath_protobuf", "xpath_print_document",
"xpath_protobuf_file", "xpath_protobuf_type", "xpath_protobuf_import_paths":
"value_field_name", "wavefront_source_override", "wavefront_use_strict", "wavefront_disable_prefix_conversion":
// ignore fields that are common to all plugins.
default:

View File

@ -406,7 +406,6 @@ func TestConfig_ParserInterfaceNewFormat(t *testing.T) {
}
override := map[string]struct {
cfg *parsers.Config
param map[string]interface{}
mask []string
}{
@ -420,11 +419,6 @@ func TestConfig_ParserInterfaceNewFormat(t *testing.T) {
mask: []string{"Now"},
},
"xpath_protobuf": {
cfg: &parsers.Config{
MetricName: "parser_test_new",
XPathProtobufFile: "testdata/addressbook.proto",
XPathProtobufType: "addressbook.AddressBook",
},
param: map[string]interface{}{
"ProtobufMessageDef": "testdata/addressbook.proto",
"ProtobufMessageType": "addressbook.AddressBook",
@ -435,10 +429,6 @@ func TestConfig_ParserInterfaceNewFormat(t *testing.T) {
expected := make([]telegraf.Parser, 0, len(formats))
for _, format := range formats {
formatCfg := &cfg
settings, hasOverride := override[format]
if hasOverride && settings.cfg != nil {
formatCfg = settings.cfg
}
formatCfg.DataFormat = format
logger := models.NewLogger("parsers", format, cfg.MetricName)
@ -555,7 +545,6 @@ func TestConfig_ParserInterfaceOldFormat(t *testing.T) {
}
override := map[string]struct {
cfg *parsers.Config
param map[string]interface{}
mask []string
}{
@ -569,11 +558,6 @@ func TestConfig_ParserInterfaceOldFormat(t *testing.T) {
mask: []string{"Now"},
},
"xpath_protobuf": {
cfg: &parsers.Config{
MetricName: "parser_test_new",
XPathProtobufFile: "testdata/addressbook.proto",
XPathProtobufType: "addressbook.AddressBook",
},
param: map[string]interface{}{
"ProtobufMessageDef": "testdata/addressbook.proto",
"ProtobufMessageType": "addressbook.AddressBook",
@ -584,10 +568,6 @@ func TestConfig_ParserInterfaceOldFormat(t *testing.T) {
expected := make([]telegraf.Parser, 0, len(formats))
for _, format := range formats {
formatCfg := &cfg
settings, hasOverride := override[format]
if hasOverride && settings.cfg != nil {
formatCfg = settings.cfg
}
formatCfg.DataFormat = format
logger := models.NewLogger("parsers", format, cfg.MetricName)

View File

@ -3,4 +3,5 @@ package all
import (
//Blank imports for plugins to register themselves
_ "github.com/influxdata/telegraf/plugins/parsers/csv"
_ "github.com/influxdata/telegraf/plugins/parsers/xpath"
)

View File

@ -19,7 +19,6 @@ import (
"github.com/influxdata/telegraf/plugins/parsers/prometheusremotewrite"
"github.com/influxdata/telegraf/plugins/parsers/value"
"github.com/influxdata/telegraf/plugins/parsers/wavefront"
"github.com/influxdata/telegraf/plugins/parsers/xpath"
)
// Creator is the function to create a new parser
@ -184,12 +183,12 @@ type Config struct {
ValueFieldName string `toml:"value_field_name"`
// XPath configuration
XPathPrintDocument bool `toml:"xpath_print_document"`
XPathProtobufFile string `toml:"xpath_protobuf_file"`
XPathProtobufType string `toml:"xpath_protobuf_type"`
XPathProtobufImportPaths []string `toml:"xpath_protobuf_import_paths"`
XPathAllowEmptySelection bool `toml:"xpath_allow_empty_selection"`
XPathConfig []XPathConfig
XPathPrintDocument bool `toml:"xpath_print_document"`
XPathProtobufFile string `toml:"xpath_protobuf_file"`
XPathProtobufType string `toml:"xpath_protobuf_type"`
XPathProtobufImportPaths []string `toml:"xpath_protobuf_import_paths"`
XPathAllowEmptySelection bool `toml:"xpath_allow_empty_selection"`
XPathConfig []XPathConfig `toml:"xpath"`
// JSONPath configuration
JSONV2Config []JSONV2Config `toml:"json_v2"`
@ -201,7 +200,29 @@ type Config struct {
LogFmtTagKeys []string `toml:"logfmt_tag_keys"`
}
type XPathConfig xpath.Config
// XPathConfig definition for backward compatibitlity ONLY.
// We need this here to avoid cyclic dependencies. However, we need
// to move this to plugins/parsers/xpath once we deprecate parser
// construction via `NewParser()`.
type XPathConfig struct {
MetricQuery string `toml:"metric_name"`
Selection string `toml:"metric_selection"`
Timestamp string `toml:"timestamp"`
TimestampFmt string `toml:"timestamp_format"`
Tags map[string]string `toml:"tags"`
Fields map[string]string `toml:"fields"`
FieldsInt map[string]string `toml:"fields_int"`
FieldSelection string `toml:"field_selection"`
FieldNameQuery string `toml:"field_name"`
FieldValueQuery string `toml:"field_value"`
FieldNameExpand bool `toml:"field_name_expansion"`
TagSelection string `toml:"tag_selection"`
TagNameQuery string `toml:"tag_name"`
TagValueQuery string `toml:"tag_value"`
TagNameExpand bool `toml:"tag_name_expansion"`
}
type JSONV2Config struct {
json_v2.Config
@ -280,17 +301,6 @@ func NewParser(config *Config) (Parser, error) {
)
case "prometheusremotewrite":
parser, err = NewPrometheusRemoteWriteParser(config.DefaultTags)
case "xml", "xpath_json", "xpath_msgpack", "xpath_protobuf":
parser = &xpath.Parser{
Format: config.DataFormat,
ProtobufMessageDef: config.XPathProtobufFile,
ProtobufMessageType: config.XPathProtobufType,
ProtobufImportPaths: config.XPathProtobufImportPaths,
PrintDocument: config.XPathPrintDocument,
DefaultTags: config.DefaultTags,
AllowEmptySelection: config.XPathAllowEmptySelection,
Configs: NewXPathParserConfigs(config.MetricName, config.XPathConfig),
}
case "json_v2":
parser, err = NewJSONPathParser(config.JSONV2Config)
default:
@ -429,17 +439,6 @@ func NewPrometheusRemoteWriteParser(defaultTags map[string]string) (Parser, erro
}, nil
}
func NewXPathParserConfigs(metricName string, cfgs []XPathConfig) []xpath.Config {
// Convert the config formats which is a one-to-one copy
configs := make([]xpath.Config, 0, len(cfgs))
for _, cfg := range cfgs {
config := xpath.Config(cfg)
config.MetricDefaultName = metricName
configs = append(configs, config)
}
return configs
}
func NewJSONPathParser(jsonv2config []JSONV2Config) (Parser, error) {
configs := make([]json_v2.Config, len(jsonv2config))
for i, cfg := range jsonv2config {

View File

@ -2,8 +2,11 @@ package parsers_test
import (
"reflect"
"sync"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
@ -15,6 +18,8 @@ func TestRegistry_BackwardCompatibility(t *testing.T) {
cfg := &parsers.Config{
MetricName: "parser_compatibility_test",
CSVHeaderRowCount: 42,
XPathProtobufFile: "xpath/testcases/protos/addressbook.proto",
XPathProtobufType: "addressbook.AddressBook",
}
// Some parsers need certain settings to not error. Furthermore, we
@ -29,6 +34,12 @@ func TestRegistry_BackwardCompatibility(t *testing.T) {
},
mask: []string{"TimeFunc"},
},
"xpath_protobuf": {
param: map[string]interface{}{
"ProtobufMessageDef": cfg.XPathProtobufFile,
"ProtobufMessageType": cfg.XPathProtobufType,
},
},
}
for name, creator := range parsers.Parsers {
@ -52,19 +63,23 @@ func TestRegistry_BackwardCompatibility(t *testing.T) {
actual, err := parsers.NewParser(cfg)
require.NoError(t, err)
// Compare with mask
if settings, found := override[name]; found {
a := reflect.Indirect(reflect.ValueOf(actual))
e := reflect.Indirect(reflect.ValueOf(expected))
for _, key := range settings.mask {
af := a.FieldByName(key)
ef := e.FieldByName(key)
v := reflect.Zero(ef.Type())
af.Set(v)
ef.Set(v)
}
// Determine the underlying type of the parser
stype := reflect.Indirect(reflect.ValueOf(expected)).Interface()
// Ignore all unexported fields and fields not relevant for functionality
options := []cmp.Option{
cmpopts.IgnoreUnexported(stype),
cmpopts.IgnoreTypes(sync.Mutex{}),
cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
}
require.EqualValuesf(t, expected, actual, "format %q", name)
// Add overrides and masks to compare options
if settings, found := override[name]; found {
options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
}
// Do a manual comparision as require.EqualValues will also work on unexported fields
// that cannot be cleared or ignored.
diff := cmp.Diff(expected, actual, options...)
require.Emptyf(t, diff, "Difference for %q", name)
}
}

View File

@ -1,6 +1,7 @@
package xpath
import (
"errors"
"fmt"
"strconv"
"strings"
@ -11,6 +12,7 @@ import (
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/metric"
"github.com/influxdata/telegraf/plugins/parsers"
)
type dataNode interface{}
@ -24,39 +26,25 @@ type dataDocument interface {
}
type Parser struct {
Format string
ProtobufMessageDef string
ProtobufMessageType string
ProtobufImportPaths []string
PrintDocument bool
AllowEmptySelection bool
Configs []Config
DefaultTags map[string]string
Log telegraf.Logger
Format string `toml:"-"`
ProtobufMessageDef string `toml:"xpath_protobuf_file"`
ProtobufMessageType string `toml:"xpath_protobuf_type"`
ProtobufImportPaths []string `toml:"xpath_protobuf_import_paths"`
PrintDocument bool `toml:"xpath_print_document"`
AllowEmptySelection bool `toml:"xpath_allow_empty_selection"`
Configs []Config `toml:"xpath"`
DefaultMetricName string `toml:"-"`
DefaultTags map[string]string `toml:"-"`
Log telegraf.Logger `toml:"-"`
document dataDocument
}
type Config struct {
MetricDefaultName string `toml:"-"`
MetricQuery string `toml:"metric_name"`
Selection string `toml:"metric_selection"`
Timestamp string `toml:"timestamp"`
TimestampFmt string `toml:"timestamp_format"`
Tags map[string]string `toml:"tags"`
Fields map[string]string `toml:"fields"`
FieldsInt map[string]string `toml:"fields_int"`
FieldSelection string `toml:"field_selection"`
FieldNameQuery string `toml:"field_name"`
FieldValueQuery string `toml:"field_value"`
FieldNameExpand bool `toml:"field_name_expansion"`
TagSelection string `toml:"tag_selection"`
TagNameQuery string `toml:"tag_name"`
TagValueQuery string `toml:"tag_value"`
TagNameExpand bool `toml:"tag_name_expansion"`
}
// Config definition
// This should be replaced by the actual definition once
// the compatibitlity-code is removed.
// Please check plugins/parsers/registry.go for now.
type Config parsers.XPathConfig
func (p *Parser) Init() error {
switch p.Format {
@ -81,6 +69,11 @@ func (p *Parser) Init() error {
return fmt.Errorf("unknown data-format %q for xpath parser", p.Format)
}
// Make sure we do have a metric name
if p.DefaultMetricName == "" {
return errors.New("missing default metric name")
}
return nil
}
@ -129,7 +122,6 @@ func (p *Parser) Parse(buf []byte) ([]telegraf.Metric, error) {
}
func (p *Parser) ParseLine(line string) (telegraf.Metric, error) {
metrics, err := p.Parse([]byte(line))
if err != nil {
return nil, err
@ -155,7 +147,7 @@ func (p *Parser) parseQuery(starttime time.Time, doc, selected dataNode, config
// Determine the metric name. If a query was specified, use the result of this query and the default metric name
// otherwise.
metricname = config.MetricDefaultName
metricname = p.DefaultMetricName
if len(config.MetricQuery) > 0 {
v, err := p.executeQuery(doc, selected, config.MetricQuery)
if err != nil {
@ -512,3 +504,62 @@ func (p *Parser) debugEmptyQuery(operation string, root dataNode, initialquery s
}
}
}
func init() {
// Register all variants
parsers.Add("xml",
func(defaultMetricName string) telegraf.Parser {
return &Parser{
Format: "xml",
DefaultMetricName: defaultMetricName,
}
},
)
parsers.Add("xpath_json",
func(defaultMetricName string) telegraf.Parser {
return &Parser{
Format: "xpath_json",
DefaultMetricName: defaultMetricName,
}
},
)
parsers.Add("xpath_msgpack",
func(defaultMetricName string) telegraf.Parser {
return &Parser{
Format: "xpath_msgpack",
DefaultMetricName: defaultMetricName,
}
},
)
parsers.Add("xpath_protobuf",
func(defaultMetricName string) telegraf.Parser {
return &Parser{
Format: "xpath_protobuf",
DefaultMetricName: defaultMetricName,
}
},
)
}
// InitFromConfig is a compatibitlity function to construct the parser the old way
func (p *Parser) InitFromConfig(config *parsers.Config) error {
p.Format = config.DataFormat
if p.Format == "xpath_protobuf" {
p.ProtobufMessageDef = config.XPathProtobufFile
p.ProtobufMessageType = config.XPathProtobufType
}
p.PrintDocument = config.XPathPrintDocument
p.DefaultMetricName = config.MetricName
p.DefaultTags = config.DefaultTags
// Convert the config formats which is a one-to-one copy
if len(config.XPathConfig) > 0 {
p.Configs = make([]Config, 0, len(config.XPathConfig))
for _, cfg := range config.XPathConfig {
config := Config(cfg)
p.Configs = append(p.Configs, config)
}
}
return p.Init()
}

View File

@ -126,7 +126,12 @@ func TestParseInvalidXML(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "xml",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
_, err := parser.ParseLine(tt.input)
@ -149,8 +154,7 @@ func TestInvalidTypeQueriesFail(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
FieldsInt: map[string]string{
"a": "/Device_1/value_string",
},
@ -163,7 +167,12 @@ func TestInvalidTypeQueriesFail(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "xml",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
_, err := parser.ParseLine(tt.input)
@ -186,8 +195,7 @@ func TestInvalidTypeQueries(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"a": "number(/Device_1/value_string)",
},
@ -208,8 +216,7 @@ func TestInvalidTypeQueries(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"a": "boolean(/Device_1/value_string)",
},
@ -229,7 +236,12 @@ func TestInvalidTypeQueries(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.ParseLine(tt.input)
@ -253,8 +265,7 @@ func TestParseTimestamps(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
},
},
defaultTags: map[string]string{},
@ -270,9 +281,8 @@ func TestParseTimestamps(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
TimestampFmt: "unix",
Timestamp: "/Device_1/Timestamp_unix",
TimestampFmt: "unix",
},
},
defaultTags: map[string]string{},
@ -288,9 +298,8 @@ func TestParseTimestamps(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix_ms",
TimestampFmt: "unix_ms",
Timestamp: "/Device_1/Timestamp_unix_ms",
TimestampFmt: "unix_ms",
},
},
defaultTags: map[string]string{},
@ -306,9 +315,8 @@ func TestParseTimestamps(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix_us",
TimestampFmt: "unix_us",
Timestamp: "/Device_1/Timestamp_unix_us",
TimestampFmt: "unix_us",
},
},
defaultTags: map[string]string{},
@ -324,9 +332,8 @@ func TestParseTimestamps(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix_ns",
TimestampFmt: "unix_ns",
Timestamp: "/Device_1/Timestamp_unix_ns",
TimestampFmt: "unix_ns",
},
},
defaultTags: map[string]string{},
@ -342,9 +349,8 @@ func TestParseTimestamps(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_iso",
TimestampFmt: "2006-01-02T15:04:05Z",
Timestamp: "/Device_1/Timestamp_iso",
TimestampFmt: "2006-01-02T15:04:05Z",
},
},
defaultTags: map[string]string{},
@ -359,7 +365,12 @@ func TestParseTimestamps(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.ParseLine(tt.input)
@ -383,8 +394,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"a": "/Device_1/value_int",
"b": "/Device_1/value_float",
@ -411,8 +421,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"a": "number(Device_1/value_int)",
"b": "number(/Device_1/value_float)",
@ -439,8 +448,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"b": "number(/Device_1/value_float)",
"c": "boolean(/Device_1/value_bool)",
@ -469,8 +477,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"x": "substring-before(/Device_1/value_position, ';')",
"y": "substring-after(/Device_1/value_position, ';')",
@ -493,8 +500,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"x": "number(substring-before(/Device_1/value_position, ';'))",
"y": "number(substring-after(/Device_1/value_position, ';'))",
@ -517,8 +523,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
FieldsInt: map[string]string{
"x": "substring-before(/Device_1/value_position, ';')",
"y": "substring-after(/Device_1/value_position, ';')",
@ -541,8 +546,7 @@ func TestParseSingleValues(t *testing.T) {
input: singleMetricValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix",
Timestamp: "/Device_1/Timestamp_unix",
Tags: map[string]string{
"state": "/Device_1/State",
"name": "substring-after(/Device_1/Name, ' ')",
@ -564,7 +568,12 @@ func TestParseSingleValues(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.ParseLine(tt.input)
@ -588,8 +597,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
},
},
defaultTags: map[string]string{},
@ -605,9 +613,8 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_iso/@value",
TimestampFmt: "2006-01-02T15:04:05Z",
Timestamp: "/Device_1/Timestamp_iso/@value",
TimestampFmt: "2006-01-02T15:04:05Z",
},
},
defaultTags: map[string]string{},
@ -623,8 +630,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
Fields: map[string]string{
"a": "/Device_1/attr_int/@_",
"b": "/Device_1/attr_float/@_",
@ -651,8 +657,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
Fields: map[string]string{
"a": "number(/Device_1/attr_int/@_)",
"b": "number(/Device_1/attr_float/@_)",
@ -679,8 +684,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
Fields: map[string]string{
"b": "number(/Device_1/attr_float/@_)",
"c": "boolean(/Device_1/attr_bool/@_)",
@ -709,8 +713,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
Fields: map[string]string{
"name": "substring-after(/Device_1/Name/@value, ' ')",
},
@ -731,8 +734,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
Tags: map[string]string{
"state": "/Device_1/State/@_",
"name": "substring-after(/Device_1/Name/@value, ' ')",
@ -755,8 +757,7 @@ func TestParseSingleAttributes(t *testing.T) {
input: singleMetricAttributesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Device_1/Timestamp_unix/@value",
Timestamp: "/Device_1/Timestamp_unix/@value",
Fields: map[string]string{
"a": "/Device_1/attr_bool_numeric/@_ = 1",
},
@ -776,7 +777,12 @@ func TestParseSingleAttributes(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.ParseLine(tt.input)
@ -800,8 +806,7 @@ func TestParseMultiValues(t *testing.T) {
input: singleMetricMultiValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Timestamp/@value",
Timestamp: "/Timestamp/@value",
Fields: map[string]string{
"a": "number(/Device/Value[1])",
"b": "number(/Device/Value[2])",
@ -832,8 +837,7 @@ func TestParseMultiValues(t *testing.T) {
input: singleMetricMultiValuesXML,
configs: []Config{
{
MetricDefaultName: "test",
Timestamp: "/Timestamp/@value",
Timestamp: "/Timestamp/@value",
FieldsInt: map[string]string{
"a": "/Device/Value[1]",
"b": "/Device/Value[2]",
@ -863,7 +867,12 @@ func TestParseMultiValues(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.ParseLine(tt.input)
@ -887,9 +896,8 @@ func TestParseMultiNodes(t *testing.T) {
input: multipleNodesXML,
configs: []Config{
{
MetricDefaultName: "test",
Selection: "/Device",
Timestamp: "/Timestamp/@value",
Selection: "/Device",
Timestamp: "/Timestamp/@value",
Fields: map[string]string{
"value": "number(Value)",
"active": "Active = 1",
@ -976,7 +984,12 @@ func TestParseMultiNodes(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.Parse([]byte(tt.input))
@ -1000,9 +1013,8 @@ func TestParseMetricQuery(t *testing.T) {
input: metricNameQueryXML,
configs: []Config{
{
MetricDefaultName: "test",
MetricQuery: "name(/Device_1/Metric/@*[1])",
Timestamp: "/Device_1/Timestamp_unix",
MetricQuery: "name(/Device_1/Metric/@*[1])",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"value": "/Device_1/Metric/@*[1]",
},
@ -1023,9 +1035,8 @@ func TestParseMetricQuery(t *testing.T) {
input: metricNameQueryXML,
configs: []Config{
{
MetricDefaultName: "test",
MetricQuery: "'the_metric'",
Timestamp: "/Device_1/Timestamp_unix",
MetricQuery: "'the_metric'",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"value": "/Device_1/Metric/@*[1]",
},
@ -1045,7 +1056,12 @@ func TestParseMetricQuery(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: tt.defaultTags, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: tt.defaultTags,
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
actual, err := parser.ParseLine(tt.input)
@ -1068,9 +1084,8 @@ func TestParseErrors(t *testing.T) {
input: metricNameQueryXML,
configs: []Config{
{
MetricDefaultName: "test",
MetricQuery: "arbitrary",
Timestamp: "/Device_1/Timestamp_unix",
MetricQuery: "arbitrary",
Timestamp: "/Device_1/Timestamp_unix",
Fields: map[string]string{
"value": "/Device_1/Metric/@*[1]",
},
@ -1082,7 +1097,12 @@ func TestParseErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: map[string]string{}, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: map[string]string{},
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
_, err := parser.ParseLine(tt.input)
@ -1150,12 +1170,17 @@ func TestEmptySelection(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, DefaultTags: map[string]string{}, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "test",
Configs: tt.configs,
DefaultTags: map[string]string{},
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
_, err := parser.Parse([]byte(tt.input))
require.Error(t, err)
require.Equal(t, "cannot parse with empty selection node", err.Error())
require.Equal(t, err.Error(), "cannot parse with empty selection node")
})
}
}
@ -1218,7 +1243,13 @@ func TestEmptySelectionAllowed(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := &Parser{Configs: tt.configs, AllowEmptySelection: true, DefaultTags: map[string]string{}, Log: testutil.Logger{Name: "parsers.xml"}}
parser := &Parser{
DefaultMetricName: "xml",
Configs: tt.configs,
AllowEmptySelection: true,
DefaultTags: map[string]string{},
Log: testutil.Logger{Name: "parsers.xml"},
}
require.NoError(t, parser.Init())
_, err := parser.Parse([]byte(tt.input))
@ -1277,7 +1308,6 @@ func TestTestCases(t *testing.T) {
filename := filepath.FromSlash(tt.filename)
cfg, header, err := loadTestConfiguration(filename)
require.NoError(t, err)
cfg.MetricDefaultName = "xml"
// Load the xml-content
input, err := testutil.ParseRawLinesFrom(header, "File:")
@ -1315,7 +1345,12 @@ func TestTestCases(t *testing.T) {
expectedErrors, _ := testutil.ParseRawLinesFrom(header, "Expected Error:")
// Setup the parser and run it.
metricName := "xml"
if fileformat != "" {
metricName = fileformat
}
parser := &Parser{
DefaultMetricName: metricName,
Format: fileformat,
ProtobufMessageDef: pbmsgdef,
ProtobufMessageType: pbmsgtype,
@ -1340,6 +1375,7 @@ func TestTestCases(t *testing.T) {
func TestProtobufImporting(t *testing.T) {
// Setup the parser and run it.
parser := &Parser{
DefaultMetricName: "xpath_protobuf",
Format: "xpath_protobuf",
ProtobufMessageDef: "person.proto",
ProtobufMessageType: "importtest.Person",