Segment labels: Docker
This commit is contained in:
parent
c762b9bb2e
commit
4802484729
28 changed files with 4095 additions and 2464 deletions
|
@ -36,13 +36,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
// 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>.
|
||||
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
|
||||
PortRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.port$`)
|
||||
|
||||
// RegexpBaseFrontendErrorPage used to extract error pages from service's label
|
||||
RegexpBaseFrontendErrorPage = regexp.MustCompile(`^frontend\.errors\.(?P<name>[^ .]+)\.(?P<field>[^ .]+)$`)
|
||||
|
||||
|
@ -56,15 +49,6 @@ var (
|
|||
RegexpFrontendRateLimit = regexp.MustCompile(`^traefik\.frontend\.rateLimit\.rateSet\.(?P<name>[^ .]+)\.(?P<field>[^ .]+)$`)
|
||||
)
|
||||
|
||||
// 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 {
|
||||
|
@ -245,56 +229,6 @@ func HasPrefixP(labels *map[string]string, prefix string) bool {
|
|||
return HasPrefix(*labels, prefix)
|
||||
}
|
||||
|
||||
// FindServiceSubmatch split service label
|
||||
func FindServiceSubmatch(name string) []string {
|
||||
matches := ServicesPropertiesRegexp.FindStringSubmatch(name)
|
||||
if matches == nil ||
|
||||
strings.HasPrefix(name, TraefikFrontend+".") ||
|
||||
strings.HasPrefix(name, TraefikBackend+".") {
|
||||
return nil
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// ExtractServiceProperties Extract services labels
|
||||
func ExtractServiceProperties(labels map[string]string) ServiceProperties {
|
||||
v := make(ServiceProperties)
|
||||
|
||||
for name, value := range labels {
|
||||
matches := FindServiceSubmatch(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)
|
||||
}
|
||||
|
||||
// ParseErrorPages parse error pages to create ErrorPage struct
|
||||
func ParseErrorPages(labels map[string]string, labelPrefix string, labelRegex *regexp.Regexp) map[string]*types.ErrorPage {
|
||||
var errorPages map[string]*types.ErrorPage
|
||||
|
@ -420,14 +354,3 @@ func SplitAndTrimString(base string, sep string) []string {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -731,11 +731,11 @@ func TestExtractServiceProperties(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected ServiceProperties
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "empty labels map",
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
|
@ -744,8 +744,8 @@ func TestExtractServiceProperties(t *testing.T) {
|
|||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: ServiceProperties{
|
||||
"foo": ServicePropertyValues{
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
|
@ -761,7 +761,7 @@ func TestExtractServiceProperties(t *testing.T) {
|
|||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
|
@ -779,11 +779,11 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
expected ServiceProperties
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
|
@ -792,8 +792,8 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
|||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: ServiceProperties{
|
||||
"foo": ServicePropertyValues{
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
|
@ -809,7 +809,7 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
|||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
|
@ -1137,3 +1137,91 @@ func TestParseRateSets(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractTraefikLabels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
prefix string
|
||||
originLabels map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
prefix: "traefik",
|
||||
originLabels: nil,
|
||||
expected: SegmentProperties{"": {}},
|
||||
},
|
||||
{
|
||||
desc: "container labels",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"frontend.priority": "foo", // missing prefix: skip
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"": {
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: only segment no default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: use default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: several segments",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := ExtractTraefikLabels(test.originLabels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,6 @@ const (
|
|||
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
||||
SuffixFrontendRedirectPermanent = "frontend.redirect.permanent"
|
||||
SuffixFrontendRule = "frontend.rule"
|
||||
SuffixFrontendRuleType = "frontend.rule.type"
|
||||
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange"
|
||||
TraefikDomain = Prefix + SuffixDomain
|
||||
TraefikEnable = Prefix + SuffixEnable
|
||||
|
@ -96,6 +95,7 @@ const (
|
|||
TraefikBackendBufferingRetryExpression = Prefix + SuffixBackendBufferingRetryExpression
|
||||
TraefikFrontend = Prefix + SuffixFrontend
|
||||
TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic
|
||||
TraefikFrontendBackend = Prefix + SuffixFrontendBackend
|
||||
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
||||
TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert
|
||||
|
@ -106,9 +106,7 @@ const (
|
|||
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
||||
TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent
|
||||
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
||||
TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType // k8s only
|
||||
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
|
||||
TraefikFrontendHeaders = Prefix + SuffixFrontendHeaders
|
||||
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
||||
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
||||
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
||||
|
|
167
provider/label/segment.go
Normal file
167
provider/label/segment.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// SegmentPropertiesRegexp used to extract the name of the segment and the name of the property for this segment
|
||||
// All properties are under the format traefik.<segment_name>.frontend.*= except the port/portIndex/weight/protocol/backend directly after traefik.<segment_name>.
|
||||
SegmentPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<segment_name>.+?)\.(?P<property_name>port|portIndex|weight|protocol|backend|frontend\.(.+))$`)
|
||||
|
||||
// PortRegexp used to extract the port label of the segment
|
||||
PortRegexp = regexp.MustCompile(`^traefik\.(?P<segment_name>.+?)\.port$`)
|
||||
)
|
||||
|
||||
// SegmentPropertyValues is a map of segment properties
|
||||
// an example value is: weight=42
|
||||
type SegmentPropertyValues map[string]string
|
||||
|
||||
// SegmentProperties is a map of segment properties per segment,
|
||||
// which we can get with label[segmentName][propertyName].
|
||||
// It yields a property value.
|
||||
type SegmentProperties map[string]SegmentPropertyValues
|
||||
|
||||
// FindSegmentSubmatch split segment labels
|
||||
func FindSegmentSubmatch(name string) []string {
|
||||
matches := SegmentPropertiesRegexp.FindStringSubmatch(name)
|
||||
if matches == nil ||
|
||||
strings.HasPrefix(name, TraefikFrontend+".") ||
|
||||
strings.HasPrefix(name, TraefikBackend+".") {
|
||||
return nil
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// ExtractServicePropertiesP Extract services labels
|
||||
// Deprecated
|
||||
func ExtractServicePropertiesP(labels *map[string]string) SegmentProperties {
|
||||
if labels == nil {
|
||||
return make(SegmentProperties)
|
||||
}
|
||||
return ExtractServiceProperties(*labels)
|
||||
}
|
||||
|
||||
// ExtractServiceProperties Extract services labels
|
||||
// Deprecated
|
||||
func ExtractServiceProperties(labels map[string]string) SegmentProperties {
|
||||
v := make(SegmentProperties)
|
||||
|
||||
for name, value := range labels {
|
||||
matches := FindSegmentSubmatch(name)
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var segmentName string
|
||||
var propertyName string
|
||||
for i, name := range SegmentPropertiesRegexp.SubexpNames() {
|
||||
// the group 0 is anonymous because it's always the root expression
|
||||
if i != 0 {
|
||||
if name == "segment_name" {
|
||||
segmentName = matches[i]
|
||||
} else if name == "property_name" {
|
||||
propertyName = matches[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := v[segmentName]; !ok {
|
||||
v[segmentName] = make(SegmentPropertyValues)
|
||||
}
|
||||
v[segmentName][propertyName] = value
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// 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"
|
||||
// Deprecated
|
||||
func GetServiceLabel(labelName, serviceName string) string {
|
||||
if len(serviceName) > 0 {
|
||||
property := strings.TrimPrefix(labelName, Prefix)
|
||||
return Prefix + serviceName + "." + property
|
||||
}
|
||||
return labelName
|
||||
}
|
||||
|
||||
// ExtractTraefikLabels transform labels to segment labels
|
||||
func ExtractTraefikLabels(originLabels map[string]string) SegmentProperties {
|
||||
allLabels := make(SegmentProperties)
|
||||
|
||||
if _, ok := allLabels[""]; !ok {
|
||||
allLabels[""] = make(SegmentPropertyValues)
|
||||
}
|
||||
|
||||
for name, value := range originLabels {
|
||||
if !strings.HasPrefix(name, Prefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
matches := FindSegmentSubmatch(name)
|
||||
if matches == nil {
|
||||
// Classic labels
|
||||
allLabels[""][name] = value
|
||||
} else {
|
||||
// segments labels
|
||||
var segmentName string
|
||||
var propertyName string
|
||||
for i, name := range SegmentPropertiesRegexp.SubexpNames() {
|
||||
// the group 0 is anonymous because it's always the root expression
|
||||
if i != 0 {
|
||||
if name == "segment_name" {
|
||||
segmentName = matches[i]
|
||||
} else if name == "property_name" {
|
||||
propertyName = matches[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := allLabels[segmentName]; !ok {
|
||||
allLabels[segmentName] = make(SegmentPropertyValues)
|
||||
}
|
||||
allLabels[segmentName][Prefix+propertyName] = value
|
||||
}
|
||||
}
|
||||
log.Debug(originLabels, allLabels)
|
||||
|
||||
allLabels.mergeDefault()
|
||||
|
||||
return allLabels
|
||||
}
|
||||
|
||||
func (s SegmentProperties) mergeDefault() {
|
||||
// if SegmentProperties contains the default segment, merge each segments with the default segment
|
||||
if defaultLabels, okDefault := s[""]; okDefault {
|
||||
|
||||
segmentsNames := s.GetSegmentNames()
|
||||
if len(defaultLabels) > 0 {
|
||||
for _, name := range segmentsNames {
|
||||
segmentLabels := s[name]
|
||||
for key, value := range defaultLabels {
|
||||
if _, ok := segmentLabels[key]; !ok {
|
||||
segmentLabels[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(segmentsNames) > 1 {
|
||||
delete(s, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSegmentNames get all segment names
|
||||
func (s SegmentProperties) GetSegmentNames() []string {
|
||||
var names []string
|
||||
for name := range s {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue