mirror of
https://github.com/containous/traefik.git
synced 2024-12-22 13:34:03 +03:00
feat: labels/annotations parser.
This commit is contained in:
parent
dc74f76a03
commit
ce6bbbaa33
@ -56,7 +56,7 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
|
|||||||
}
|
}
|
||||||
|
|
||||||
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
|
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
|
||||||
config, err := fileConfig.LoadConfig()
|
config, err := fileConfig.BuildConfiguration()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -95,8 +95,8 @@ func (p *Provider) scanTable(client *dynamoClient) ([]map[string]*dynamodb.Attri
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadDynamoConfig retrieves items from dynamodb and converts them into Backends and Frontends in a Configuration
|
// buildConfiguration retrieves items from dynamodb and converts them into Backends and Frontends in a Configuration
|
||||||
func (p *Provider) loadDynamoConfig(client *dynamoClient) (*types.Configuration, error) {
|
func (p *Provider) buildConfiguration(client *dynamoClient) (*types.Configuration, error) {
|
||||||
items, err := p.scanTable(client)
|
items, err := p.scanTable(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -167,7 +167,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||||||
return handleCanceled(ctx, err)
|
return handleCanceled(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration, err := p.loadDynamoConfig(awsClient)
|
configuration, err := p.buildConfiguration(awsClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleCanceled(ctx, err)
|
return handleCanceled(ctx, err)
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||||||
log.Debug("Watching Provider...")
|
log.Debug("Watching Provider...")
|
||||||
select {
|
select {
|
||||||
case <-reload.C:
|
case <-reload.C:
|
||||||
configuration, err := p.loadDynamoConfig(awsClient)
|
configuration, err := p.buildConfiguration(awsClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleCanceled(ctx, err)
|
return handleCanceled(ctx, err)
|
||||||
}
|
}
|
||||||
|
@ -84,14 +84,14 @@ func (m *mockDynamoDBClient) ScanPages(input *dynamodb.ScanInput, fn func(*dynam
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadDynamoConfigSuccessful(t *testing.T) {
|
func TestBuildConfigurationSuccessful(t *testing.T) {
|
||||||
dbiface := &dynamoClient{
|
dbiface := &dynamoClient{
|
||||||
db: &mockDynamoDBClient{
|
db: &mockDynamoDBClient{
|
||||||
testWithError: false,
|
testWithError: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
provider := Provider{}
|
provider := Provider{}
|
||||||
loadedConfig, err := provider.loadDynamoConfig(dbiface)
|
loadedConfig, err := provider.buildConfiguration(dbiface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -108,14 +108,14 @@ func TestLoadDynamoConfigSuccessful(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadDynamoConfigFailure(t *testing.T) {
|
func TestBuildConfigurationFailure(t *testing.T) {
|
||||||
dbiface := &dynamoClient{
|
dbiface := &dynamoClient{
|
||||||
db: &mockDynamoDBClient{
|
db: &mockDynamoDBClient{
|
||||||
testWithError: true,
|
testWithError: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
provider := Provider{}
|
provider := Provider{}
|
||||||
_, err := provider.loadDynamoConfig(dbiface)
|
_, err := provider.buildConfiguration(dbiface)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error")
|
t.Fatal("Expected error")
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ type Provider struct {
|
|||||||
// Provide allows the file provider to provide configurations to traefik
|
// Provide allows the file provider to provide configurations to traefik
|
||||||
// using the given configuration channel.
|
// using the given configuration channel.
|
||||||
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
||||||
configuration, err := p.LoadConfig()
|
configuration, err := p.BuildConfiguration()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -52,9 +52,9 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig loads configuration either from file or a directory specified by 'Filename'/'Directory'
|
// BuildConfiguration loads configuration either from file or a directory specified by 'Filename'/'Directory'
|
||||||
// and returns a 'Configuration' object
|
// and returns a 'Configuration' object
|
||||||
func (p *Provider) LoadConfig() (*types.Configuration, error) {
|
func (p *Provider) BuildConfiguration() (*types.Configuration, error) {
|
||||||
if p.Directory != "" {
|
if p.Directory != "" {
|
||||||
return loadFileConfigFromDirectory(p.Directory, nil)
|
return loadFileConfigFromDirectory(p.Directory, nil)
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ func (p *Provider) watcherCallback(configurationChan chan<- types.ConfigMessage,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration, err := p.LoadConfig()
|
configuration, err := p.BuildConfiguration()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error occurred during watcher callback: %s", err)
|
log.Errorf("Error occurred during watcher callback: %s", err)
|
||||||
|
273
provider/label/label.go
Normal file
273
provider/label/label.go
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
package label
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mapEntrySeparator = "||"
|
||||||
|
mapValueSeparator = ":"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
const (
|
||||||
|
DefaultWeight = "0"
|
||||||
|
DefaultProtocol = "http"
|
||||||
|
DefaultPassHostHeader = "true"
|
||||||
|
DefaultFrontendPriority = "0"
|
||||||
|
DefaultCircuitBreakerExpression = "NetworkErrorRatio() > 1"
|
||||||
|
DefaultFrontendRedirect = ""
|
||||||
|
DefaultBackendLoadBalancerMethod = "wrr"
|
||||||
|
DefaultBackendMaxconnExtractorFunc = "request.host"
|
||||||
|
DefaultBackendLoadbalancerStickinessCookieName = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServicesPropertiesRegexp used to extract the name of the service and the name of the property for this service
|
||||||
|
// All properties are under the format traefik.<servicename>.frontend.*= except the port/portIndex/weight/protocol/backend directly after traefik.<servicename>.
|
||||||
|
var ServicesPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.(?P<property_name>port|portIndex|weight|protocol|backend|frontend\.(.+))$`)
|
||||||
|
|
||||||
|
// PortRegexp used to extract the port label of the service
|
||||||
|
var PortRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.port$`)
|
||||||
|
|
||||||
|
// ServicePropertyValues is a map of services properties
|
||||||
|
// an example value is: weight=42
|
||||||
|
type ServicePropertyValues map[string]string
|
||||||
|
|
||||||
|
// ServiceProperties is a map of service properties per service,
|
||||||
|
// which we can get with label[serviceName][propertyName].
|
||||||
|
// It yields a property value.
|
||||||
|
type ServiceProperties map[string]ServicePropertyValues
|
||||||
|
|
||||||
|
// GetStringValue get string value associated to a label
|
||||||
|
func GetStringValue(labels map[string]string, labelName string, defaultValue string) string {
|
||||||
|
if value, ok := labels[labelName]; ok && len(value) > 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringValueP get string value associated to a label
|
||||||
|
func GetStringValueP(labels *map[string]string, labelName string, defaultValue string) string {
|
||||||
|
if labels == nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return GetStringValue(*labels, labelName, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBoolValue get bool value associated to a label
|
||||||
|
func GetBoolValue(labels map[string]string, labelName string, defaultValue bool) bool {
|
||||||
|
rawValue, ok := labels[labelName]
|
||||||
|
if ok {
|
||||||
|
v, err := strconv.ParseBool(rawValue)
|
||||||
|
if err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntValue get int value associated to a label
|
||||||
|
func GetIntValue(labels map[string]string, labelName string, defaultValue int) int {
|
||||||
|
if rawValue, ok := labels[labelName]; ok {
|
||||||
|
value, err := strconv.Atoi(rawValue)
|
||||||
|
if err == nil {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
log.Errorf("Unable to parse %q: %q, falling back to %v. %v", labelName, rawValue, defaultValue, err)
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntValueP get int value associated to a label
|
||||||
|
func GetIntValueP(labels *map[string]string, labelName string, defaultValue int) int {
|
||||||
|
if labels == nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return GetIntValue(*labels, labelName, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64Value get int64 value associated to a label
|
||||||
|
func GetInt64Value(labels map[string]string, labelName string, defaultValue int64) int64 {
|
||||||
|
if rawValue, ok := labels[labelName]; ok {
|
||||||
|
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
log.Errorf("Unable to parse %q: %q, falling back to %v. %v", labelName, rawValue, defaultValue, err)
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64ValueP get int64 value associated to a label
|
||||||
|
func GetInt64ValueP(labels *map[string]string, labelName string, defaultValue int64) int64 {
|
||||||
|
if labels == nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return GetInt64Value(*labels, labelName, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSliceStringValue get a slice of string associated to a label
|
||||||
|
func GetSliceStringValue(labels map[string]string, labelName string) []string {
|
||||||
|
var value []string
|
||||||
|
|
||||||
|
if values, ok := labels[labelName]; ok {
|
||||||
|
value = SplitAndTrimString(values, ",")
|
||||||
|
|
||||||
|
if len(value) == 0 {
|
||||||
|
log.Debugf("Could not load %q.", labelName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSliceStringValueP get a slice of string value associated to a label
|
||||||
|
func GetSliceStringValueP(labels *map[string]string, labelName string) []string {
|
||||||
|
if labels == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return GetSliceStringValue(*labels, labelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMapValue get Map value associated to a label
|
||||||
|
func GetMapValue(labels map[string]string, labelName string) map[string]string {
|
||||||
|
if values, ok := labels[labelName]; ok {
|
||||||
|
|
||||||
|
if len(values) == 0 {
|
||||||
|
log.Errorf("Missing value for %q, skipping...", labelName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mapValue := make(map[string]string)
|
||||||
|
|
||||||
|
for _, parts := range strings.Split(values, mapEntrySeparator) {
|
||||||
|
pair := strings.SplitN(parts, mapValueSeparator, 2)
|
||||||
|
if len(pair) != 2 {
|
||||||
|
log.Warnf("Could not load %q: %q, skipping...", labelName, parts)
|
||||||
|
} else {
|
||||||
|
mapValue[http.CanonicalHeaderKey(strings.TrimSpace(pair[0]))] = strings.TrimSpace(pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mapValue) == 0 {
|
||||||
|
log.Errorf("Could not load %q, skipping...", labelName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return mapValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringMultipleStrict get multiple string values associated to several labels
|
||||||
|
// Fail if one label is missing
|
||||||
|
func GetStringMultipleStrict(labels map[string]string, labelNames ...string) (map[string]string, error) {
|
||||||
|
foundLabels := map[string]string{}
|
||||||
|
for _, name := range labelNames {
|
||||||
|
value := GetStringValue(labels, name, "")
|
||||||
|
// Error out only if one of them is not defined.
|
||||||
|
if len(value) == 0 {
|
||||||
|
return nil, fmt.Errorf("label not found: %s", name)
|
||||||
|
}
|
||||||
|
foundLabels[name] = value
|
||||||
|
}
|
||||||
|
return foundLabels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has Check if a value is associated to a label
|
||||||
|
func Has(labels map[string]string, labelName string) bool {
|
||||||
|
value, ok := labels[labelName]
|
||||||
|
return ok && len(value) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasP Check if a value is associated to a label
|
||||||
|
func HasP(labels *map[string]string, labelName string) bool {
|
||||||
|
if labels == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return Has(*labels, labelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractServiceProperties Extract services labels
|
||||||
|
func ExtractServiceProperties(labels map[string]string) ServiceProperties {
|
||||||
|
v := make(ServiceProperties)
|
||||||
|
|
||||||
|
for name, value := range labels {
|
||||||
|
matches := ServicesPropertiesRegexp.FindStringSubmatch(name)
|
||||||
|
if matches == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceName string
|
||||||
|
var propertyName string
|
||||||
|
for i, name := range ServicesPropertiesRegexp.SubexpNames() {
|
||||||
|
if i != 0 {
|
||||||
|
if name == "service_name" {
|
||||||
|
serviceName = matches[i]
|
||||||
|
} else if name == "property_name" {
|
||||||
|
propertyName = matches[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := v[serviceName]; !ok {
|
||||||
|
v[serviceName] = make(ServicePropertyValues)
|
||||||
|
}
|
||||||
|
v[serviceName][propertyName] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractServicePropertiesP Extract services labels
|
||||||
|
func ExtractServicePropertiesP(labels *map[string]string) ServiceProperties {
|
||||||
|
if labels == nil {
|
||||||
|
return make(ServiceProperties)
|
||||||
|
}
|
||||||
|
return ExtractServiceProperties(*labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled Check if a container is enabled in Træfik
|
||||||
|
func IsEnabled(labels map[string]string, exposedByDefault bool) bool {
|
||||||
|
return GetBoolValue(labels, TraefikEnable, exposedByDefault)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabledP Check if a container is enabled in Træfik
|
||||||
|
func IsEnabledP(labels *map[string]string, exposedByDefault bool) bool {
|
||||||
|
if labels == nil {
|
||||||
|
return exposedByDefault
|
||||||
|
}
|
||||||
|
return IsEnabled(*labels, exposedByDefault)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitAndTrimString splits separatedString at the separator character and trims each
|
||||||
|
// piece, filtering out empty pieces. Returns the list of pieces or nil if the input
|
||||||
|
// did not contain a non-empty piece.
|
||||||
|
func SplitAndTrimString(base string, sep string) []string {
|
||||||
|
var trimmedStrings []string
|
||||||
|
|
||||||
|
for _, s := range strings.Split(base, sep) {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if len(s) > 0 {
|
||||||
|
trimmedStrings = append(trimmedStrings, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmedStrings
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServiceLabel converts a key value of Label*, given a serviceName,
|
||||||
|
// into a pattern <LabelPrefix>.<serviceName>.<property>
|
||||||
|
// i.e. For LabelFrontendRule and serviceName=app it will return "traefik.app.frontend.rule"
|
||||||
|
func GetServiceLabel(labelName, serviceName string) string {
|
||||||
|
if len(serviceName) > 0 {
|
||||||
|
property := strings.TrimPrefix(labelName, Prefix)
|
||||||
|
return Prefix + serviceName + "." + property
|
||||||
|
}
|
||||||
|
return labelName
|
||||||
|
}
|
969
provider/label/label_test.go
Normal file
969
provider/label/label_test.go
Normal file
@ -0,0 +1,969 @@
|
|||||||
|
package label
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSplitAndTrimString(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
input string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty string",
|
||||||
|
input: "",
|
||||||
|
expected: nil,
|
||||||
|
}, {
|
||||||
|
desc: "one piece",
|
||||||
|
input: "foo",
|
||||||
|
expected: []string{"foo"},
|
||||||
|
}, {
|
||||||
|
desc: "two pieces",
|
||||||
|
input: "foo,bar",
|
||||||
|
expected: []string{"foo", "bar"},
|
||||||
|
}, {
|
||||||
|
desc: "three pieces",
|
||||||
|
input: "foo,bar,zoo",
|
||||||
|
expected: []string{"foo", "bar", "zoo"},
|
||||||
|
}, {
|
||||||
|
desc: "two pieces with whitespace",
|
||||||
|
input: " foo , bar ",
|
||||||
|
expected: []string{"foo", "bar"},
|
||||||
|
}, {
|
||||||
|
desc: "consecutive commas",
|
||||||
|
input: " foo ,, bar ",
|
||||||
|
expected: []string{"foo", "bar"},
|
||||||
|
}, {
|
||||||
|
desc: "consecutive commas with whitespace",
|
||||||
|
input: " foo , , bar ",
|
||||||
|
expected: []string{"foo", "bar"},
|
||||||
|
}, {
|
||||||
|
desc: "leading and trailing commas",
|
||||||
|
input: ",, foo , , bar,, , ",
|
||||||
|
expected: []string{"foo", "bar"},
|
||||||
|
}, {
|
||||||
|
desc: "no valid pieces",
|
||||||
|
input: ", , , ,, ,",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
actual := SplitAndTrimString(test.input, ",")
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetStringValue(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty labels map",
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: "default",
|
||||||
|
expected: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existing label",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: "default",
|
||||||
|
expected: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "non existing label",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "fii",
|
||||||
|
defaultValue: "default",
|
||||||
|
expected: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetStringValue(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetStringValueP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil labels map",
|
||||||
|
labels: nil,
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: "default",
|
||||||
|
expected: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existing label",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: "default",
|
||||||
|
expected: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "non existing label",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "fii",
|
||||||
|
defaultValue: "default",
|
||||||
|
expected: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetStringValueP(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetBoolValue(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue bool
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid boolean value",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "valid boolean value: true",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "true",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: false,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "valid boolean value: false",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "false",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: true,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetBoolValue(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetIntValue(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "negative int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "-1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "positive int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetIntValue(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetIntValueP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil map",
|
||||||
|
labels: nil,
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "negative int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "-1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "positive int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetIntValueP(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetInt64Value(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue int64
|
||||||
|
expected int64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "negative int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "-1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "positive int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetInt64Value(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetInt64ValueP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
labelName string
|
||||||
|
defaultValue int64
|
||||||
|
expected int64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil map",
|
||||||
|
labels: nil,
|
||||||
|
labelName: "foo",
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "negative int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "-1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "positive int value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "1",
|
||||||
|
},
|
||||||
|
defaultValue: 666,
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetInt64ValueP(test.labels, test.labelName, test.defaultValue)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSliceStringValue(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "empty value",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "one value, not split",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: []string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "several values",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar,bir ,bur",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: []string{"bar", "bir", "bur"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetSliceStringValue(test.labels, test.labelName)
|
||||||
|
assert.EqualValues(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSliceStringValueP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
labelName string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil map",
|
||||||
|
labels: nil,
|
||||||
|
labelName: "foo",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "one value, not split",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: []string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "several values",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar,bir ,bur",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: []string{"bar", "bir", "bur"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetSliceStringValueP(test.labels, test.labelName)
|
||||||
|
assert.EqualValues(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMapValue(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
expected map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label with empty entry",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "",
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label with invalid entry",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label with empty value",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar:",
|
||||||
|
},
|
||||||
|
expected: map[string]string{
|
||||||
|
"Bar": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "one entry",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": " Access-Control-Allow-Methods:POST,GET,OPTIONS ",
|
||||||
|
},
|
||||||
|
expected: map[string]string{
|
||||||
|
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "several entry",
|
||||||
|
labelName: "foo",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
expected: map[string]string{
|
||||||
|
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||||
|
"Content-Type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetMapValue(test.labels, test.labelName)
|
||||||
|
assert.EqualValues(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetStringMultipleStrict(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelNames []string
|
||||||
|
expected map[string]string
|
||||||
|
expectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty labels names and empty labels map",
|
||||||
|
labels: map[string]string{},
|
||||||
|
expected: map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "empty labels names",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"fii": "bir",
|
||||||
|
},
|
||||||
|
expected: map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "one label missing",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"fii": "bir",
|
||||||
|
"fyy": "byr",
|
||||||
|
},
|
||||||
|
labelNames: []string{"foo", "fii", "fuu"},
|
||||||
|
expected: nil,
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "all labels are present",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"fii": "bir",
|
||||||
|
"fyy": "byr",
|
||||||
|
},
|
||||||
|
labelNames: []string{"foo", "fii"},
|
||||||
|
expected: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"fii": "bir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got, err := GetStringMultipleStrict(test.labels, test.labelNames...)
|
||||||
|
if (err != nil) != test.expectedErr {
|
||||||
|
t.Errorf("error = %v, wantErr %v", err, test.expectedErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.EqualValues(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHas(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
labelName string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil labels map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nonexistent label",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "fii",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label with empty value",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo": "",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := Has(test.labels, test.labelName)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
labelName string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil labels map",
|
||||||
|
labelName: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nonexistent label",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "fii",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent label with empty value",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo": "",
|
||||||
|
},
|
||||||
|
labelName: "foo",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := HasP(test.labels, test.labelName)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractServiceProperties(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
expected ServiceProperties
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty labels map",
|
||||||
|
expected: ServiceProperties{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "valid label names",
|
||||||
|
labels: map[string]string{
|
||||||
|
"traefik.foo.port": "bar",
|
||||||
|
"traefik.foo.frontend.bar": "1bar",
|
||||||
|
"traefik.foo.frontend.": "2bar",
|
||||||
|
},
|
||||||
|
expected: ServiceProperties{
|
||||||
|
"foo": ServicePropertyValues{
|
||||||
|
"port": "bar",
|
||||||
|
"frontend.bar": "1bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid label names",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo.frontend.bar": "1bar",
|
||||||
|
"traefik.foo.frontend.": "2bar",
|
||||||
|
"traefik.foo.port.bar": "barbar",
|
||||||
|
"traefik.foo.frontend": "0bar",
|
||||||
|
},
|
||||||
|
expected: ServiceProperties{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := ExtractServiceProperties(test.labels)
|
||||||
|
assert.EqualValues(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractServicePropertiesP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
expected ServiceProperties
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil labels map",
|
||||||
|
expected: ServiceProperties{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "valid label names",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"traefik.foo.port": "bar",
|
||||||
|
"traefik.foo.frontend.bar": "1bar",
|
||||||
|
"traefik.foo.frontend.": "2bar",
|
||||||
|
},
|
||||||
|
expected: ServiceProperties{
|
||||||
|
"foo": ServicePropertyValues{
|
||||||
|
"port": "bar",
|
||||||
|
"frontend.bar": "1bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid label names",
|
||||||
|
labels: &map[string]string{
|
||||||
|
"foo.frontend.bar": "1bar",
|
||||||
|
"traefik.foo.frontend.": "2bar",
|
||||||
|
"traefik.foo.port.bar": "barbar",
|
||||||
|
"traefik.foo.frontend": "0bar",
|
||||||
|
},
|
||||||
|
expected: ServiceProperties{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := ExtractServicePropertiesP(test.labels)
|
||||||
|
assert.EqualValues(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsEnabled(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
exposedByDefault bool
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty labels map & exposedByDefault true",
|
||||||
|
exposedByDefault: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "empty labels map & exposedByDefault false",
|
||||||
|
exposedByDefault: false,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault false and label enable true",
|
||||||
|
labels: map[string]string{
|
||||||
|
TraefikEnable: "true",
|
||||||
|
},
|
||||||
|
exposedByDefault: false,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault false and label enable false",
|
||||||
|
labels: map[string]string{
|
||||||
|
TraefikEnable: "false",
|
||||||
|
},
|
||||||
|
exposedByDefault: false,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault true and label enable false",
|
||||||
|
labels: map[string]string{
|
||||||
|
TraefikEnable: "false",
|
||||||
|
},
|
||||||
|
exposedByDefault: true,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault true and label enable true",
|
||||||
|
labels: map[string]string{
|
||||||
|
TraefikEnable: "true",
|
||||||
|
},
|
||||||
|
exposedByDefault: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := IsEnabled(test.labels, test.exposedByDefault)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsEnabledP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels *map[string]string
|
||||||
|
exposedByDefault bool
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil labels map & exposedByDefault true",
|
||||||
|
exposedByDefault: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nil labels map & exposedByDefault false",
|
||||||
|
exposedByDefault: false,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault false and label enable true",
|
||||||
|
labels: &map[string]string{
|
||||||
|
TraefikEnable: "true",
|
||||||
|
},
|
||||||
|
exposedByDefault: false,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault false and label enable false",
|
||||||
|
labels: &map[string]string{
|
||||||
|
TraefikEnable: "false",
|
||||||
|
},
|
||||||
|
exposedByDefault: false,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault true and label enable false",
|
||||||
|
labels: &map[string]string{
|
||||||
|
TraefikEnable: "false",
|
||||||
|
},
|
||||||
|
exposedByDefault: true,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "exposedByDefault true and label enable true",
|
||||||
|
labels: &map[string]string{
|
||||||
|
TraefikEnable: "true",
|
||||||
|
},
|
||||||
|
exposedByDefault: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := IsEnabledP(test.labels, test.exposedByDefault)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetServiceLabel(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labelName string
|
||||||
|
serviceName string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "without service name",
|
||||||
|
labelName: TraefikPort,
|
||||||
|
expected: TraefikPort,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with service name",
|
||||||
|
labelName: TraefikPort,
|
||||||
|
serviceName: "bar",
|
||||||
|
expected: "traefik.bar.port",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := GetServiceLabel(test.labelName, test.serviceName)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
105
provider/label/names.go
Normal file
105
provider/label/names.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package label
|
||||||
|
|
||||||
|
// Traefik labels
|
||||||
|
const (
|
||||||
|
Prefix = "traefik."
|
||||||
|
SuffixBackend = "backend"
|
||||||
|
SuffixDomain = "domain"
|
||||||
|
SuffixEnable = "enable"
|
||||||
|
SuffixPort = "port"
|
||||||
|
SuffixPortIndex = "portIndex"
|
||||||
|
SuffixProtocol = "protocol"
|
||||||
|
SuffixTags = "tags"
|
||||||
|
SuffixWeight = "weight"
|
||||||
|
SuffixBackendID = "backend.id"
|
||||||
|
SuffixBackendCircuitBreaker = "backend.circuitbreaker"
|
||||||
|
SuffixBackendCircuitBreakerExpression = "backend.circuitbreaker.expression"
|
||||||
|
SuffixBackendHealthCheckPath = "backend.healthcheck.path"
|
||||||
|
SuffixBackendHealthCheckInterval = "backend.healthcheck.interval"
|
||||||
|
SuffixBackendLoadBalancerMethod = "backend.loadbalancer.method"
|
||||||
|
SuffixBackendLoadBalancerSticky = "backend.loadbalancer.sticky"
|
||||||
|
SuffixBackendLoadBalancerStickiness = "backend.loadbalancer.stickiness"
|
||||||
|
SuffixBackendLoadBalancerStickinessCookieName = "backend.loadbalancer.stickiness.cookieName"
|
||||||
|
SuffixBackendMaxConnAmount = "backend.maxconn.amount"
|
||||||
|
SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc"
|
||||||
|
SuffixFrontendAuthBasic = "frontend.auth.basic"
|
||||||
|
SuffixFrontendBackend = "frontend.backend"
|
||||||
|
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
||||||
|
SuffixFrontendRequestHeaders = "frontend.headers.customRequestHeaders"
|
||||||
|
SuffixFrontendResponseHeaders = "frontend.headers.customResponseHeaders"
|
||||||
|
SuffixFrontendHeadersAllowedHosts = "frontend.headers.allowedHosts"
|
||||||
|
SuffixFrontendHeadersHostsProxyHeaders = "frontend.headers.hostsProxyHeaders"
|
||||||
|
SuffixFrontendHeadersSSLRedirect = "frontend.headers.SSLRedirect"
|
||||||
|
SuffixFrontendHeadersSSLTemporaryRedirect = "frontend.headers.SSLTemporaryRedirect"
|
||||||
|
SuffixFrontendHeadersSSLHost = "frontend.headers.SSLHost"
|
||||||
|
SuffixFrontendHeadersSSLProxyHeaders = "frontend.headers.SSLProxyHeaders"
|
||||||
|
SuffixFrontendHeadersSTSSeconds = "frontend.headers.STSSeconds"
|
||||||
|
SuffixFrontendHeadersSTSIncludeSubdomains = "frontend.headers.STSIncludeSubdomains"
|
||||||
|
SuffixFrontendHeadersSTSPreload = "frontend.headers.STSPreload"
|
||||||
|
SuffixFrontendHeadersForceSTSHeader = "frontend.headers.forceSTSHeader"
|
||||||
|
SuffixFrontendHeadersFrameDeny = "frontend.headers.frameDeny"
|
||||||
|
SuffixFrontendHeadersCustomFrameOptionsValue = "frontend.headers.customFrameOptionsValue"
|
||||||
|
SuffixFrontendHeadersContentTypeNosniff = "frontend.headers.contentTypeNosniff"
|
||||||
|
SuffixFrontendHeadersBrowserXSSFilter = "frontend.headers.browserXSSFilter"
|
||||||
|
SuffixFrontendHeadersContentSecurityPolicy = "frontend.headers.contentSecurityPolicy"
|
||||||
|
SuffixFrontendHeadersPublicKey = "frontend.headers.publicKey"
|
||||||
|
SuffixFrontendHeadersReferrerPolicy = "frontend.headers.referrerPolicy"
|
||||||
|
SuffixFrontendHeadersIsDevelopment = "frontend.headers.isDevelopment"
|
||||||
|
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
||||||
|
SuffixFrontendPassTLSCert = "frontend.passTLSCert"
|
||||||
|
SuffixFrontendPriority = "frontend.priority"
|
||||||
|
SuffixFrontendRedirect = "frontend.redirect"
|
||||||
|
SuffixFrontendRule = "frontend.rule"
|
||||||
|
SuffixFrontendRuleType = "frontend.rule.type"
|
||||||
|
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange"
|
||||||
|
SuffixFrontendValue = "frontend.value"
|
||||||
|
TraefikDomain = Prefix + SuffixDomain
|
||||||
|
TraefikEnable = Prefix + SuffixEnable
|
||||||
|
TraefikPort = Prefix + SuffixPort
|
||||||
|
TraefikPortIndex = Prefix + SuffixPortIndex
|
||||||
|
TraefikProtocol = Prefix + SuffixProtocol
|
||||||
|
TraefikTags = Prefix + SuffixTags
|
||||||
|
TraefikWeight = Prefix + SuffixWeight
|
||||||
|
TraefikBackend = Prefix + SuffixBackend
|
||||||
|
TraefikBackendID = Prefix + SuffixBackendID
|
||||||
|
TraefikBackendCircuitBreaker = Prefix + SuffixBackendCircuitBreaker
|
||||||
|
TraefikBackendCircuitBreakerExpression = Prefix + SuffixBackendCircuitBreakerExpression
|
||||||
|
TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath
|
||||||
|
TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval
|
||||||
|
TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod
|
||||||
|
TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky
|
||||||
|
TraefikBackendLoadBalancerStickiness = Prefix + SuffixBackendLoadBalancerStickiness
|
||||||
|
TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName
|
||||||
|
TraefikBackendMaxConnAmount = Prefix + SuffixBackendMaxConnAmount
|
||||||
|
TraefikBackendMaxConnExtractorFunc = Prefix + SuffixBackendMaxConnExtractorFunc
|
||||||
|
TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic
|
||||||
|
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||||
|
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
||||||
|
TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert
|
||||||
|
TraefikFrontendPriority = Prefix + SuffixFrontendPriority
|
||||||
|
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
||||||
|
TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType
|
||||||
|
TraefikFrontendRedirect = Prefix + SuffixFrontendRedirect
|
||||||
|
TraefikFrontendValue = Prefix + SuffixFrontendValue
|
||||||
|
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
|
||||||
|
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
||||||
|
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
||||||
|
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
||||||
|
TraefikFrontendHostsProxyHeaders = Prefix + SuffixFrontendHeadersHostsProxyHeaders
|
||||||
|
TraefikFrontendSSLRedirect = Prefix + SuffixFrontendHeadersSSLRedirect
|
||||||
|
TraefikFrontendSSLTemporaryRedirect = Prefix + SuffixFrontendHeadersSSLTemporaryRedirect
|
||||||
|
TraefikFrontendSSLHost = Prefix + SuffixFrontendHeadersSSLHost
|
||||||
|
TraefikFrontendSSLProxyHeaders = Prefix + SuffixFrontendHeadersSSLProxyHeaders
|
||||||
|
TraefikFrontendSTSSeconds = Prefix + SuffixFrontendHeadersSTSSeconds
|
||||||
|
TraefikFrontendSTSIncludeSubdomains = Prefix + SuffixFrontendHeadersSTSIncludeSubdomains
|
||||||
|
TraefikFrontendSTSPreload = Prefix + SuffixFrontendHeadersSTSPreload
|
||||||
|
TraefikFrontendForceSTSHeader = Prefix + SuffixFrontendHeadersForceSTSHeader
|
||||||
|
TraefikFrontendFrameDeny = Prefix + SuffixFrontendHeadersFrameDeny
|
||||||
|
TraefikFrontendCustomFrameOptionsValue = Prefix + SuffixFrontendHeadersCustomFrameOptionsValue
|
||||||
|
TraefikFrontendContentTypeNosniff = Prefix + SuffixFrontendHeadersContentTypeNosniff
|
||||||
|
TraefikFrontendBrowserXSSFilter = Prefix + SuffixFrontendHeadersBrowserXSSFilter
|
||||||
|
TraefikFrontendContentSecurityPolicy = Prefix + SuffixFrontendHeadersContentSecurityPolicy
|
||||||
|
TraefikFrontendPublicKey = Prefix + SuffixFrontendHeadersPublicKey
|
||||||
|
TraefikFrontendReferrerPolicy = Prefix + SuffixFrontendHeadersReferrerPolicy
|
||||||
|
TraefikFrontendIsDevelopment = Prefix + SuffixFrontendHeadersIsDevelopment
|
||||||
|
)
|
@ -888,7 +888,7 @@ func (s *Server) getRoundTripper(entryPointName string, globalConfiguration conf
|
|||||||
return s.defaultForwardingRoundTripper, nil
|
return s.defaultForwardingRoundTripper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
|
// loadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
|
||||||
// provider configurations.
|
// provider configurations.
|
||||||
func (s *Server) loadConfig(configurations types.Configurations, globalConfiguration configuration.GlobalConfiguration) (map[string]*serverEntryPoint, error) {
|
func (s *Server) loadConfig(configurations types.Configurations, globalConfiguration configuration.GlobalConfiguration) (map[string]*serverEntryPoint, error) {
|
||||||
serverEntryPoints := s.buildEntryPoints(globalConfiguration)
|
serverEntryPoints := s.buildEntryPoints(globalConfiguration)
|
||||||
|
Loading…
Reference in New Issue
Block a user