Skip to content

Commit

Permalink
Merge pull request #48 from calyptia/validate-props
Browse files Browse the repository at this point in the history
expose properties validator
  • Loading branch information
niedbalski authored Nov 11, 2022
2 parents eb2e002 + 3421535 commit ea014ba
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 54 deletions.
20 changes: 19 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package fluentbitconfig

import "github.com/calyptia/go-fluentbit-config/property"
import (
"fmt"

"github.com/calyptia/go-fluentbit-config/property"
)

type Config struct {
Env property.Properties `json:"env,omitempty" yaml:"env,omitempty"`
Expand All @@ -18,3 +22,17 @@ type Pipeline struct {
}

type ByName map[string]property.Properties

// Name from properties.
func Name(props property.Properties) string {
nameVal, ok := props.Get("name")
if !ok {
return ""
}

if name, ok := nameVal.(string); ok {
return name
}

return fmt.Sprintf("%v", nameVal)
}
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ module github.com/calyptia/go-fluentbit-config

go 1.19

require gopkg.in/yaml.v3 v3.0.1
require (
github.com/alecthomas/assert/v2 v2.2.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/alecthomas/assert/v2 v2.2.0 // indirect
github.com/alecthomas/repr v0.1.0 // indirect
github.com/alecthomas/repr v0.1.1 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
github.com/alecthomas/assert/v2 v2.2.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs=
github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
43 changes: 42 additions & 1 deletion schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
"strings"
)

//go:embed schemas/1.9.json
Expand Down Expand Up @@ -53,7 +54,17 @@ type SchemaProperties struct {
NetworkTLS []SchemaOptions `json:"network_tls"`
}

func (pp SchemaProperties) All() []SchemaOptions {
func (sec SchemaSection) findOptions(name string) (SchemaOptions, bool) {
for _, opts := range sec.Properties.all() {
if strings.EqualFold(opts.Name, name) {
return opts, true
}
}

return SchemaOptions{}, false
}

func (pp SchemaProperties) all() []SchemaOptions {
var out []SchemaOptions
out = append(out, pp.Options...)
out = append(out, pp.Networking...)
Expand All @@ -68,6 +79,36 @@ type SchemaOptions struct {
Type string `json:"type"`
}

func (s Schema) findSection(kind SectionKind, name string) (SchemaSection, bool) {
sections, ok := s.findSections(kind)
if !ok {
return SchemaSection{}, false
}

for _, section := range sections {
if strings.EqualFold(section.Name, name) {
return section, true
}
}

return SchemaSection{}, false
}

func (s Schema) findSections(kind SectionKind) ([]SchemaSection, bool) {
switch kind {
case SectionKindCustom:
return s.Customs, true
case SectionKindInput:
return s.Inputs, true
case SectionKindFilter:
return s.Filters, true
case SectionKindOutput:
return s.Outputs, true
default:
return nil, false
}
}

func (s *Schema) InjectLTSPlugins() {
// See https://github.com/calyptia/core-images/blob/main/container/plugins/plugins.json
s.Inputs = append(s.Inputs, SchemaSection{
Expand Down
14 changes: 14 additions & 0 deletions section.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package fluentbitconfig

type SectionKind string

func (k SectionKind) String() string { return string(k) }

const (
SectionKindService SectionKind = "service"
SectionKindCustom SectionKind = "custom"
SectionKindInput SectionKind = "input"
SectionKindParser SectionKind = "parser"
SectionKindFilter SectionKind = "filter"
SectionKindOutput SectionKind = "output"
)
104 changes: 58 additions & 46 deletions validator.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package fluentbitconfig

import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"

"github.com/calyptia/go-fluentbit-config/property"
)

// Validate with the default schema.
Expand All @@ -18,49 +21,11 @@ func (c Config) Validate() error {
// has a property named "pid" that is of integer type,
// it must be a valid integer.
func (c Config) ValidateWithSchema(schema Schema) error {
validate := func(kind string, byNames []ByName, schemaSections []SchemaSection) error {
validate := func(kind SectionKind, byNames []ByName) error {
for _, byName := range byNames {
for name, props := range byName {
var foundPlugin bool
for _, schemaSection := range schemaSections {
if !strings.EqualFold(name, schemaSection.Name) {
continue
}

for _, p := range props {
if isCommonProperty(p.Key) {
continue
}

if isCloudVariable(p.Value) {
continue
}

var foundProp bool
for _, schemaOptions := range schemaSection.Properties.All() {
if !strings.EqualFold(p.Key, schemaOptions.Name) {
continue
}

if !valid(schemaOptions, p.Value) {
return fmt.Errorf("%s: %s: expected %q to be a valid %s, got %v", kind, name, p.Key, schemaOptions.Type, p.Value)
}

foundProp = true
break
}

if !foundProp {
return fmt.Errorf("%s: %s: unknown property %q", kind, name, p.Key)
}
}

foundPlugin = true
break
}

if !foundPlugin {
return fmt.Errorf("%s: unknown plugin %q", kind, name)
for _, props := range byName {
if err := ValidateSectionWithSchema(kind, props, schema); err != nil {
return err
}
}
}
Expand All @@ -69,26 +34,73 @@ func (c Config) ValidateWithSchema(schema Schema) error {
return nil
}

if err := validate("custom", c.Customs, schema.Customs); err != nil {
if err := validate(SectionKindCustom, c.Customs); err != nil {
return err
}

if err := validate("input", c.Pipeline.Inputs, schema.Inputs); err != nil {
if err := validate(SectionKindInput, c.Pipeline.Inputs); err != nil {
return err
}

if err := validate("filter", c.Pipeline.Filters, schema.Filters); err != nil {
if err := validate(SectionKindFilter, c.Pipeline.Filters); err != nil {
return err
}

if err := validate("output", c.Pipeline.Outputs, schema.Outputs); err != nil {
if err := validate(SectionKindOutput, c.Pipeline.Outputs); err != nil {
return err
}

// valid by default
return nil
}

func ValidateSection(kind SectionKind, props property.Properties) error {
return ValidateSectionWithSchema(kind, props, DefaultSchema)
}

func ValidateSectionWithSchema(kind SectionKind, props property.Properties, schema Schema) error {
name := Name(props)
if name == "" {
return errors.New("missing \"name\" property")
}

// If the name takes a cloud variable, it won't be on the schema,
// so we allow it.
// TODO: review whether is valid that the `name` property can take a
// cloud variable syntax.
//
// Example:
// [INPUT]
// Name {{files.myinput}}
//
// Maybe we can pass-over the actual value.
if isCloudVariable(name) {
return nil
}

section, ok := schema.findSection(kind, name)
if !ok {
return fmt.Errorf("%s: unknown plugin %q", kind, name)
}

for _, p := range props {
if isCommonProperty(p.Key) || isCloudVariable(p.Key) || isCloudVariable(p.Value) {
continue
}

opts, ok := section.findOptions(p.Key)
if !ok {
return fmt.Errorf("%s: %s: unknown property %q", kind, name, p.Key)
}

if !valid(opts, p.Value) {
return fmt.Errorf("%s: %s: expected %q to be a valid %s, got %v", kind, name, p.Key, opts.Type, p.Value)
}
}

return nil
}

func isCommonProperty(name string) bool {
for _, got := range [...]string{"name", "alias", "tag", "match", "match_Regex"} {
if strings.EqualFold(name, got) {
Expand Down
1 change: 0 additions & 1 deletion validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ func TestConfig_Validate(t *testing.T) {
Name cpu
pid 3.4
`,
// TODO: fix float not keeping its original value.
want: `input: cpu: expected "pid" to be a valid integer, got 3.4`,
},
{
Expand Down

0 comments on commit ea014ba

Please sign in to comment.