New static configuration loading system.

Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
Ludovic Fernandez 2019-06-17 11:48:05 +02:00 committed by Traefiker Bot
parent d18edd6f77
commit 8d7eccad5d
165 changed files with 10894 additions and 6076 deletions

View file

@ -10,12 +10,12 @@ import (
)
// Constraint holds a parsed constraint expression.
// FIXME replace by a string.
type Constraint struct {
Key string `export:"true"`
Key string `description:"The provider label that will be matched against. In practice, it is always 'tag'." export:"true"`
// MustMatch is true if operator is "==" or false if operator is "!="
MustMatch bool `export:"true"`
// TODO: support regex
Regex string `export:"true"`
MustMatch bool `description:"Whether the matching operator is equals or not equals." export:"true"`
Value string `description:"The value that will be matched against." export:"true"` // TODO: support regex
}
// NewConstraint receives a string and return a *Constraint, after checking syntax and parsing the constraint expression.
@ -42,7 +42,7 @@ func NewConstraint(exp string) (*Constraint, error) {
}
constraint.Key = kv[0]
constraint.Regex = kv[1]
constraint.Value = kv[1]
return constraint, nil
}
@ -51,9 +51,9 @@ func NewConstraint(exp string) (*Constraint, error) {
func (c *Constraint) String() string {
if c.MustMatch {
return c.Key + "==" + c.Regex
return c.Key + "==" + c.Value
}
return c.Key + "!=" + c.Regex
return c.Key + "!=" + c.Value
}
var _ encoding.TextUnmarshaler = (*Constraint)(nil)
@ -66,7 +66,7 @@ func (c *Constraint) UnmarshalText(text []byte) error {
}
c.Key = constraint.Key
c.MustMatch = constraint.MustMatch
c.Regex = constraint.Regex
c.Value = constraint.Value
return nil
}
@ -80,44 +80,9 @@ func (c *Constraint) MarshalText() (text []byte, err error) {
// MatchConstraintWithAtLeastOneTag tests a constraint for one single service.
func (c *Constraint) MatchConstraintWithAtLeastOneTag(tags []string) bool {
for _, tag := range tags {
if glob.Glob(c.Regex, tag) {
if glob.Glob(c.Value, tag) {
return true
}
}
return false
}
// Set []*Constraint.
func (cs *Constraints) Set(str string) error {
exps := strings.Split(str, ",")
if len(exps) == 0 {
return fmt.Errorf("bad Constraint format: %s", str)
}
for _, exp := range exps {
constraint, err := NewConstraint(exp)
if err != nil {
return err
}
*cs = append(*cs, constraint)
}
return nil
}
// Constraints holds a Constraint parser.
type Constraints []*Constraint
// Get []*Constraint
func (cs *Constraints) Get() interface{} { return []*Constraint(*cs) }
// String returns []*Constraint in string.
func (cs *Constraints) String() string { return fmt.Sprintf("%+v", *cs) }
// SetValue sets []*Constraint into the parser.
func (cs *Constraints) SetValue(val interface{}) {
*cs = val.(Constraints)
}
// Type exports the Constraints type as a string.
func (cs *Constraints) Type() string {
return "constraint"
}

View file

@ -1,44 +0,0 @@
package types
import (
"fmt"
"strings"
)
// DNSResolvers is a list of DNSes that we will try to resolve the challenged FQDN against
type DNSResolvers []string
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (r *DNSResolvers) String() string {
return strings.Join(*r, ",")
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (r *DNSResolvers) Set(value string) error {
entryPoints := strings.Split(value, ",")
if len(entryPoints) == 0 {
return fmt.Errorf("wrong DNSResolvers format: %s", value)
}
for _, entryPoint := range entryPoints {
*r = append(*r, entryPoint)
}
return nil
}
// Get return the DNSResolvers list
func (r *DNSResolvers) Get() interface{} {
return *r
}
// SetValue sets the DNSResolvers list
func (r *DNSResolvers) SetValue(val interface{}) {
*r = val.(DNSResolvers)
}
// Type is type of the struct
func (r *DNSResolvers) Type() string {
return "dnsresolvers"
}

View file

@ -7,8 +7,8 @@ import (
// Domain holds a domain name with SANs.
type Domain struct {
Main string
SANs []string
Main string `description:"Default subject name."`
SANs []string `description:"Subject alternative names."`
}
// ToStrArray convert a domain into an array of strings.

69
pkg/types/duration.go Normal file
View file

@ -0,0 +1,69 @@
package types
import (
"encoding/json"
"strconv"
"time"
)
// Duration is a custom type suitable for parsing duration values.
// It supports `time.ParseDuration`-compatible values and suffix-less digits; in
// the latter case, seconds are assumed.
type Duration time.Duration
// Set sets the duration from the given string value.
func (d *Duration) Set(s string) error {
if v, err := strconv.ParseInt(s, 10, 64); err == nil {
*d = Duration(time.Duration(v) * time.Second)
return nil
}
v, err := time.ParseDuration(s)
*d = Duration(v)
return err
}
// Get returns the duration value.
func (d *Duration) Get() interface{} { return time.Duration(*d) }
// String returns a string representation of the duration value.
func (d *Duration) String() string { return (*time.Duration)(d).String() }
// SetValue sets the duration from the given Duration-asserted value.
func (d *Duration) SetValue(val interface{}) {
*d = val.(Duration)
}
// MarshalText serialize the given duration value into a text.
func (d *Duration) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}
// UnmarshalText deserializes the given text into a duration value.
// It is meant to support TOML decoding of durations.
func (d *Duration) UnmarshalText(text []byte) error {
return d.Set(string(text))
}
// MarshalJSON serializes the given duration value.
func (d *Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Duration(*d))
}
// UnmarshalJSON deserializes the given text into a duration value.
func (d *Duration) UnmarshalJSON(text []byte) error {
if v, err := strconv.ParseInt(string(text), 10, 64); err == nil {
*d = Duration(time.Duration(v))
return nil
}
// We use json unmarshal on value because we have the quoted version
var value string
err := json.Unmarshal(text, &value)
if err != nil {
return err
}
v, err := time.ParseDuration(value)
*d = Duration(v)
return err
}

View file

@ -6,3 +6,10 @@ type HostResolverConfig struct {
ResolvConfig string `description:"resolv.conf used for DNS resolving" export:"true"`
ResolvDepth int `description:"The maximal depth of DNS recursive resolving" export:"true"`
}
// SetDefaults sets the default values.
func (h *HostResolverConfig) SetDefaults() {
h.CnameFlattening = false
h.ResolvConfig = "/etc/resolv.conf"
h.ResolvDepth = 5
}

View file

@ -1,12 +1,5 @@
package types
import (
"fmt"
"strings"
"github.com/containous/flaeg/parse"
)
const (
// AccessLogKeep is the keep string value
AccessLogKeep = "keep"
@ -16,144 +9,71 @@ const (
AccessLogRedact = "redact"
)
const (
// CommonFormat is the common logging format (CLF).
CommonFormat string = "common"
// JSONFormat is the JSON logging format.
JSONFormat string = "json"
)
// TraefikLog holds the configuration settings for the traefik logger.
type TraefikLog struct {
Level string `description:"Log level set to traefik logs." export:"true"`
FilePath string `json:"file,omitempty" description:"Traefik log file path. Stdout is used when omitted or empty"`
FilePath string `json:"file,omitempty" description:"Traefik log file path. Stdout is used when omitted or empty."`
Format string `json:"format,omitempty" description:"Traefik log format: json | common"`
}
// SetDefaults sets the default values.
func (l *TraefikLog) SetDefaults() {
l.Format = CommonFormat
l.Level = "ERROR"
}
// AccessLog holds the configuration settings for the access logger (middlewares/accesslog).
type AccessLog struct {
FilePath string `json:"file,omitempty" description:"Access log file path. Stdout is used when omitted or empty" export:"true"`
FilePath string `json:"file,omitempty" description:"Access log file path. Stdout is used when omitted or empty." export:"true"`
Format string `json:"format,omitempty" description:"Access log format: json | common" export:"true"`
Filters *AccessLogFilters `json:"filters,omitempty" description:"Access log filters, used to keep only specific access logs" export:"true"`
Fields *AccessLogFields `json:"fields,omitempty" description:"AccessLogFields" export:"true"`
BufferingSize int64 `json:"bufferingSize,omitempty" description:"Number of access log lines to process in a buffered way. Default 0." export:"true"`
Filters *AccessLogFilters `json:"filters,omitempty" description:"Access log filters, used to keep only specific access logs." export:"true"`
Fields *AccessLogFields `json:"fields,omitempty" description:"AccessLogFields." export:"true"`
BufferingSize int64 `json:"bufferingSize,omitempty" description:"Number of access log lines to process in a buffered way." export:"true"`
}
// SetDefaults sets the default values.
func (l *AccessLog) SetDefaults() {
l.Format = CommonFormat
l.FilePath = ""
l.Filters = &AccessLogFilters{}
l.Fields = &AccessLogFields{}
l.Fields.SetDefaults()
}
// AccessLogFilters holds filters configuration
type AccessLogFilters struct {
StatusCodes StatusCodes `json:"statusCodes,omitempty" description:"Keep access logs with status codes in the specified range" export:"true"`
RetryAttempts bool `json:"retryAttempts,omitempty" description:"Keep access logs when at least one retry happened" export:"true"`
MinDuration parse.Duration `json:"duration,omitempty" description:"Keep access logs when request took longer than the specified duration" export:"true"`
StatusCodes []string `json:"statusCodes,omitempty" description:"Keep access logs with status codes in the specified range." export:"true"`
RetryAttempts bool `json:"retryAttempts,omitempty" description:"Keep access logs when at least one retry happened." export:"true"`
MinDuration Duration `json:"duration,omitempty" description:"Keep access logs when request took longer than the specified duration." export:"true"`
}
// FieldHeaders holds configuration for access log headers
type FieldHeaders struct {
DefaultMode string `json:"defaultMode,omitempty" description:"Default mode for fields: keep | drop | redact" export:"true"`
Names FieldHeaderNames `json:"names,omitempty" description:"Override mode for headers" export:"true"`
}
// StatusCodes holds status codes ranges to filter access log
type StatusCodes []string
// Set adds strings elem into the the parser
// it splits str on , and ;
func (s *StatusCodes) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
*s = append(*s, slice...)
return nil
}
// Get StatusCodes
func (s *StatusCodes) Get() interface{} { return *s }
// String return slice in a string
func (s *StatusCodes) String() string { return fmt.Sprintf("%v", *s) }
// SetValue sets StatusCodes into the parser
func (s *StatusCodes) SetValue(val interface{}) {
*s = val.(StatusCodes)
}
// FieldNames holds maps of fields with specific mode
type FieldNames map[string]string
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (f *FieldNames) String() string {
return fmt.Sprintf("%+v", *f)
}
// Get return the FieldNames map
func (f *FieldNames) Get() interface{} {
return *f
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a space-separated list, so we split it.
func (f *FieldNames) Set(value string) error {
// When arguments are passed through YAML, escaped double quotes
// might be added to this string, and they would break the last
// key/value pair. This ensures the string is clean.
value = strings.Trim(value, "\"")
fields := strings.Fields(value)
for _, field := range fields {
n := strings.SplitN(field, "=", 2)
if len(n) == 2 {
(*f)[n[0]] = n[1]
}
}
return nil
}
// SetValue sets the FieldNames map with val
func (f *FieldNames) SetValue(val interface{}) {
*f = val.(FieldNames)
}
// FieldHeaderNames holds maps of fields with specific mode
type FieldHeaderNames map[string]string
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (f *FieldHeaderNames) String() string {
return fmt.Sprintf("%+v", *f)
}
// Get return the FieldHeaderNames map
func (f *FieldHeaderNames) Get() interface{} {
return *f
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a space-separated list, so we split it.
func (f *FieldHeaderNames) Set(value string) error {
// When arguments are passed through YAML, escaped double quotes
// might be added to this string, and they would break the last
// key/value pair. This ensures the string is clean.
value = strings.Trim(value, "\"")
fields := strings.Fields(value)
for _, field := range fields {
n := strings.SplitN(field, "=", 2)
(*f)[n[0]] = n[1]
}
return nil
}
// SetValue sets the FieldHeaderNames map with val
func (f *FieldHeaderNames) SetValue(val interface{}) {
*f = val.(FieldHeaderNames)
DefaultMode string `json:"defaultMode,omitempty" description:"Default mode for fields: keep | drop | redact" export:"true"`
Names map[string]string `json:"names,omitempty" description:"Override mode for headers" export:"true"`
}
// AccessLogFields holds configuration for access log fields
type AccessLogFields struct {
DefaultMode string `json:"defaultMode,omitempty" description:"Default mode for fields: keep | drop" export:"true"`
Names FieldNames `json:"names,omitempty" description:"Override mode for fields" export:"true"`
Headers *FieldHeaders `json:"headers,omitempty" description:"Headers to keep, drop or redact" export:"true"`
DefaultMode string `json:"defaultMode,omitempty" description:"Default mode for fields: keep | drop" export:"true"`
Names map[string]string `json:"names,omitempty" description:"Override mode for fields" export:"true"`
Headers *FieldHeaders `json:"headers,omitempty" description:"Headers to keep, drop or redact" export:"true"`
}
// SetDefaults sets the default values.
func (f *AccessLogFields) SetDefaults() {
f.DefaultMode = AccessLogKeep
f.Headers = &FieldHeaders{
DefaultMode: AccessLogKeep,
}
}
// Keep check if the field need to be kept or dropped

View file

@ -1,419 +0,0 @@
package types
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestStatusCodesSet(t *testing.T) {
testCases := []struct {
desc string
value string
expected StatusCodes
}{
{
desc: "One value should return StatusCodes of size 1",
value: "200",
expected: StatusCodes{"200"},
},
{
desc: "Two values separated by comma should return StatusCodes of size 2",
value: "200,400",
expected: StatusCodes{"200", "400"},
},
{
desc: "Two values separated by semicolon should return StatusCodes of size 2",
value: "200;400",
expected: StatusCodes{"200", "400"},
},
{
desc: "Three values separated by comma and semicolon should return StatusCodes of size 3",
value: "200,400;500",
expected: StatusCodes{"200", "400", "500"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
var statusCodes StatusCodes
err := statusCodes.Set(test.value)
assert.Nil(t, err)
assert.Equal(t, test.expected, statusCodes)
})
}
}
func TestStatusCodesGet(t *testing.T) {
testCases := []struct {
desc string
values StatusCodes
expected StatusCodes
}{
{
desc: "Should return 1 value",
values: StatusCodes{"200"},
expected: StatusCodes{"200"},
},
{
desc: "Should return 2 values",
values: StatusCodes{"200", "400"},
expected: StatusCodes{"200", "400"},
},
{
desc: "Should return 3 values",
values: StatusCodes{"200", "400", "500"},
expected: StatusCodes{"200", "400", "500"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := test.values.Get()
assert.Equal(t, test.expected, actual)
})
}
}
func TestStatusCodesString(t *testing.T) {
testCases := []struct {
desc string
values StatusCodes
expected string
}{
{
desc: "Should return 1 value",
values: StatusCodes{"200"},
expected: "[200]",
},
{
desc: "Should return 2 values",
values: StatusCodes{"200", "400"},
expected: "[200 400]",
},
{
desc: "Should return 3 values",
values: StatusCodes{"200", "400", "500"},
expected: "[200 400 500]",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := test.values.String()
assert.Equal(t, test.expected, actual)
})
}
}
func TestStatusCodesSetValue(t *testing.T) {
testCases := []struct {
desc string
values StatusCodes
expected StatusCodes
}{
{
desc: "Should return 1 value",
values: StatusCodes{"200"},
expected: StatusCodes{"200"},
},
{
desc: "Should return 2 values",
values: StatusCodes{"200", "400"},
expected: StatusCodes{"200", "400"},
},
{
desc: "Should return 3 values",
values: StatusCodes{"200", "400", "500"},
expected: StatusCodes{"200", "400", "500"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
var slice StatusCodes
slice.SetValue(test.values)
assert.Equal(t, test.expected, slice)
})
}
}
func TestFieldsNamesSet(t *testing.T) {
testCases := []struct {
desc string
value string
expected *FieldNames
}{
{
desc: "One value should return FieldNames of size 1",
value: "field-1=foo",
expected: &FieldNames{
"field-1": "foo",
},
},
{
desc: "Two values separated by space should return FieldNames of size 2",
value: "field-1=foo field-2=bar",
expected: &FieldNames{
"field-1": "foo",
"field-2": "bar",
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
fieldsNames := &FieldNames{}
err := fieldsNames.Set(test.value)
assert.NoError(t, err)
assert.Equal(t, test.expected, fieldsNames)
})
}
}
func TestFieldsNamesGet(t *testing.T) {
testCases := []struct {
desc string
values FieldNames
expected FieldNames
}{
{
desc: "Should return 1 value",
values: FieldNames{"field-1": "foo"},
expected: FieldNames{"field-1": "foo"},
},
{
desc: "Should return 2 values",
values: FieldNames{"field-1": "foo", "field-2": "bar"},
expected: FieldNames{"field-1": "foo", "field-2": "bar"},
},
{
desc: "Should return 3 values",
values: FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
expected: FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := test.values.Get()
assert.Equal(t, test.expected, actual)
})
}
}
func TestFieldsNamesString(t *testing.T) {
testCases := []struct {
desc string
values FieldNames
expected string
}{
{
desc: "Should return 1 value",
values: FieldNames{"field-1": "foo"},
expected: "map[field-1:foo]",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := test.values.String()
assert.Equal(t, test.expected, actual)
})
}
}
func TestFieldsNamesSetValue(t *testing.T) {
testCases := []struct {
desc string
values FieldNames
expected *FieldNames
}{
{
desc: "Should return 1 value",
values: FieldNames{"field-1": "foo"},
expected: &FieldNames{"field-1": "foo"},
},
{
desc: "Should return 2 values",
values: FieldNames{"field-1": "foo", "field-2": "bar"},
expected: &FieldNames{"field-1": "foo", "field-2": "bar"},
},
{
desc: "Should return 3 values",
values: FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
expected: &FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
fieldsNames := &FieldNames{}
fieldsNames.SetValue(test.values)
assert.Equal(t, test.expected, fieldsNames)
})
}
}
func TestFieldsHeadersNamesSet(t *testing.T) {
testCases := []struct {
desc string
value string
expected *FieldHeaderNames
}{
{
desc: "One value should return FieldNames of size 1",
value: "X-HEADER-1=foo",
expected: &FieldHeaderNames{
"X-HEADER-1": "foo",
},
},
{
desc: "Two values separated by space should return FieldNames of size 2",
value: "X-HEADER-1=foo X-HEADER-2=bar",
expected: &FieldHeaderNames{
"X-HEADER-1": "foo",
"X-HEADER-2": "bar",
},
},
{
desc: "Two values separated by space with escaped double quotes should return FieldNames of size 2",
value: "\"X-HEADER-1=foo X-HEADER-2=bar\"",
expected: &FieldHeaderNames{
"X-HEADER-1": "foo",
"X-HEADER-2": "bar",
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
headersNames := &FieldHeaderNames{}
err := headersNames.Set(test.value)
assert.NoError(t, err)
assert.Equal(t, test.expected, headersNames)
})
}
}
func TestFieldsHeadersNamesGet(t *testing.T) {
testCases := []struct {
desc string
values FieldHeaderNames
expected FieldHeaderNames
}{
{
desc: "Should return 1 value",
values: FieldHeaderNames{"X-HEADER-1": "foo"},
expected: FieldHeaderNames{"X-HEADER-1": "foo"},
},
{
desc: "Should return 2 values",
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
expected: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
},
{
desc: "Should return 3 values",
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
expected: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := test.values.Get()
assert.Equal(t, test.expected, actual)
})
}
}
func TestFieldsHeadersNamesString(t *testing.T) {
testCases := []struct {
desc string
values FieldHeaderNames
expected string
}{
{
desc: "Should return 1 value",
values: FieldHeaderNames{"X-HEADER-1": "foo"},
expected: "map[X-HEADER-1:foo]",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := test.values.String()
assert.Equal(t, test.expected, actual)
})
}
}
func TestFieldsHeadersNamesSetValue(t *testing.T) {
testCases := []struct {
desc string
values FieldHeaderNames
expected *FieldHeaderNames
}{
{
desc: "Should return 1 value",
values: FieldHeaderNames{"X-HEADER-1": "foo"},
expected: &FieldHeaderNames{"X-HEADER-1": "foo"},
},
{
desc: "Should return 2 values",
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
expected: &FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
},
{
desc: "Should return 3 values",
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
expected: &FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
headersNames := &FieldHeaderNames{}
headersNames.SetValue(test.values)
assert.Equal(t, test.expected, headersNames)
})
}
}

View file

@ -1,82 +1,79 @@
package types
import (
"fmt"
"strconv"
"strings"
"time"
)
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems
type Metrics struct {
Prometheus *Prometheus `description:"Prometheus metrics exporter type" export:"true"`
Datadog *Datadog `description:"DataDog metrics exporter type" export:"true"`
StatsD *Statsd `description:"StatsD metrics exporter type" export:"true"`
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type"`
Prometheus *Prometheus `description:"Prometheus metrics exporter type." export:"true" label:"allowEmpty"`
Datadog *Datadog `description:"DataDog metrics exporter type." export:"true" label:"allowEmpty"`
StatsD *Statsd `description:"StatsD metrics exporter type." export:"true" label:"allowEmpty"`
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type." label:"allowEmpty"`
}
// Prometheus can contain specific configuration used by the Prometheus Metrics exporter
type Prometheus struct {
Buckets Buckets `description:"Buckets for latency metrics" export:"true"`
EntryPoint string `description:"EntryPoint" export:"true"`
Middlewares []string `description:"Middlewares" export:"true"`
Buckets []float64 `description:"Buckets for latency metrics." export:"true"`
EntryPoint string `description:"EntryPoint." export:"true"`
Middlewares []string `description:"Middlewares." export:"true"`
}
// SetDefaults sets the default values.
func (p *Prometheus) SetDefaults() {
p.Buckets = []float64{0.1, 0.3, 1.2, 5}
p.EntryPoint = "traefik"
// FIXME p.EntryPoint = static.DefaultInternalEntryPointName
}
// Datadog contains address and metrics pushing interval configuration
type Datadog struct {
Address string `description:"DataDog's address"`
PushInterval string `description:"DataDog push interval" export:"true"`
Address string `description:"DataDog's address."`
PushInterval Duration `description:"DataDog push interval." export:"true"`
}
// SetDefaults sets the default values.
func (d *Datadog) SetDefaults() {
d.Address = "localhost:8125"
d.PushInterval = Duration(10 * time.Second)
}
// Statsd contains address and metrics pushing interval configuration
type Statsd struct {
Address string `description:"StatsD address"`
PushInterval string `description:"StatsD push interval" export:"true"`
Address string `description:"StatsD address."`
PushInterval Duration `description:"StatsD push interval." export:"true"`
}
// SetDefaults sets the default values.
func (s *Statsd) SetDefaults() {
s.Address = "localhost:8125"
s.PushInterval = Duration(10 * time.Second)
}
// InfluxDB contains address, login and metrics pushing interval configuration
type InfluxDB struct {
Address string `description:"InfluxDB address"`
Protocol string `description:"InfluxDB address protocol (udp or http)"`
PushInterval string `description:"InfluxDB push interval" export:"true"`
Database string `description:"InfluxDB database used when protocol is http" export:"true"`
RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http" export:"true"`
Username string `description:"InfluxDB username (only with http)" export:"true"`
Password string `description:"InfluxDB password (only with http)" export:"true"`
Address string `description:"InfluxDB address."`
Protocol string `description:"InfluxDB address protocol (udp or http)."`
PushInterval Duration `description:"InfluxDB push interval." export:"true"`
Database string `description:"InfluxDB database used when protocol is http." export:"true"`
RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." export:"true"`
Username string `description:"InfluxDB username (only with http)." export:"true"`
Password string `description:"InfluxDB password (only with http)." export:"true"`
}
// SetDefaults sets the default values.
func (i *InfluxDB) SetDefaults() {
i.Address = "localhost:8089"
i.Protocol = "udp"
i.PushInterval = Duration(10 * time.Second)
}
// Statistics provides options for monitoring request and response stats
type Statistics struct {
RecentErrors int `description:"Number of recent errors logged" export:"true"`
RecentErrors int `description:"Number of recent errors logged." export:"true"`
}
// Buckets holds Prometheus Buckets
type Buckets []float64
// Set adds strings elem into the the parser
// it splits str on "," and ";" and apply ParseFloat to string
func (b *Buckets) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
for _, bucket := range slice {
bu, err := strconv.ParseFloat(bucket, 64)
if err != nil {
return err
}
*b = append(*b, bu)
}
return nil
}
// Get []float64
func (b *Buckets) Get() interface{} { return *b }
// String return slice in a string
func (b *Buckets) String() string { return fmt.Sprintf("%v", *b) }
// SetValue sets []float64 into the parser
func (b *Buckets) SetValue(val interface{}) {
*b = val.(Buckets)
// SetDefaults sets the default values.
func (s *Statistics) SetDefaults() {
s.RecentErrors = 10
}