refactor: Consul Catalog labels.
This commit is contained in:
parent
46db91ce73
commit
ff61cc971e
14 changed files with 1715 additions and 2018 deletions
273
provider/consulcatalog/config.go
Normal file
273
provider/consulcatalog/config.go
Normal file
|
@ -0,0 +1,273 @@
|
|||
package consulcatalog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfigurationV2(catalog []catalogUpdate) *types.Configuration {
|
||||
var funcMap = template.FuncMap{
|
||||
"getAttribute": p.getAttribute,
|
||||
"getTag": getTag,
|
||||
"hasTag": hasTag,
|
||||
|
||||
// Backend functions
|
||||
"getNodeBackendName": getNodeBackendName,
|
||||
"getServiceBackendName": getServiceBackendName,
|
||||
"getBackendAddress": getBackendAddress,
|
||||
"getServerName": getServerName,
|
||||
"getCircuitBreaker": getCircuitBreaker,
|
||||
"getLoadBalancer": getLoadBalancer,
|
||||
"getMaxConn": label.GetMaxConn,
|
||||
"getHealthCheck": label.GetHealthCheck,
|
||||
"getBuffering": label.GetBuffering,
|
||||
"getServer": p.getServer,
|
||||
|
||||
// Frontend functions
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||
"getFrontEndEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getWhiteList": label.GetWhiteList,
|
||||
"getRedirect": label.GetRedirect,
|
||||
"getErrorPages": label.GetErrorPages,
|
||||
"getRateLimit": label.GetRateLimit,
|
||||
"getHeaders": label.GetHeaders,
|
||||
}
|
||||
|
||||
var allNodes []*api.ServiceEntry
|
||||
var services []*serviceUpdate
|
||||
for _, info := range catalog {
|
||||
if len(info.Nodes) > 0 {
|
||||
services = append(services, info.Service)
|
||||
allNodes = append(allNodes, info.Nodes...)
|
||||
}
|
||||
}
|
||||
// Ensure a stable ordering of nodes so that identical configurations may be detected
|
||||
sort.Sort(nodeSorter(allNodes))
|
||||
|
||||
templateObjects := struct {
|
||||
Services []*serviceUpdate
|
||||
Nodes []*api.ServiceEntry
|
||||
}{
|
||||
Services: services,
|
||||
Nodes: allNodes,
|
||||
}
|
||||
|
||||
configuration, err := p.GetConfiguration("templates/consul_catalog.tmpl", funcMap, templateObjects)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create config")
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
// Specific functions
|
||||
|
||||
func (p *Provider) getFrontendRule(service serviceUpdate) string {
|
||||
customFrontendRule := label.GetStringValue(service.TraefikLabels, label.TraefikFrontendRule, "")
|
||||
if customFrontendRule == "" {
|
||||
customFrontendRule = p.FrontEndRule
|
||||
}
|
||||
|
||||
tmpl := p.frontEndRuleTemplate
|
||||
tmpl, err := tmpl.Parse(customFrontendRule)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse Consul Catalog custom frontend rule: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
ServiceName string
|
||||
Domain string
|
||||
Attributes []string
|
||||
}{
|
||||
ServiceName: service.ServiceName,
|
||||
Domain: p.Domain,
|
||||
Attributes: service.Attributes,
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
err = tmpl.Execute(&buffer, templateObjects)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to execute Consul Catalog custom frontend rule template: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (p *Provider) getServer(node *api.ServiceEntry) types.Server {
|
||||
scheme := p.getAttribute(label.SuffixProtocol, node.Service.Tags, label.DefaultProtocol)
|
||||
address := getBackendAddress(node)
|
||||
|
||||
return types.Server{
|
||||
URL: fmt.Sprintf("%s://%s:%d", scheme, address, node.Service.Port),
|
||||
Weight: p.getWeight(node.Service.Tags),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) setupFrontEndRuleTemplate() {
|
||||
var FuncMap = template.FuncMap{
|
||||
"getAttribute": p.getAttribute,
|
||||
"getTag": getTag,
|
||||
"hasTag": hasTag,
|
||||
}
|
||||
p.frontEndRuleTemplate = template.New("consul catalog frontend rule").Funcs(FuncMap)
|
||||
}
|
||||
|
||||
// Specific functions
|
||||
|
||||
// Only for compatibility
|
||||
// Deprecated
|
||||
func getLoadBalancer(labels map[string]string) *types.LoadBalancer {
|
||||
if v, ok := labels[label.TraefikBackendLoadBalancer]; ok {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancer, label.TraefikBackendLoadBalancerMethod)
|
||||
if !label.Has(labels, label.TraefikBackendLoadBalancerMethod) {
|
||||
labels[label.TraefikBackendLoadBalancerMethod] = v
|
||||
}
|
||||
}
|
||||
|
||||
return label.GetLoadBalancer(labels)
|
||||
}
|
||||
|
||||
// Only for compatibility
|
||||
// Deprecated
|
||||
func getCircuitBreaker(labels map[string]string) *types.CircuitBreaker {
|
||||
if v, ok := labels[label.TraefikBackendCircuitBreaker]; ok {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendCircuitBreaker, label.TraefikBackendCircuitBreakerExpression)
|
||||
if !label.Has(labels, label.TraefikBackendCircuitBreakerExpression) {
|
||||
labels[label.TraefikBackendCircuitBreakerExpression] = v
|
||||
}
|
||||
}
|
||||
|
||||
return label.GetCircuitBreaker(labels)
|
||||
}
|
||||
|
||||
func getServiceBackendName(service *serviceUpdate) string {
|
||||
return strings.ToLower(service.ServiceName)
|
||||
}
|
||||
|
||||
func getNodeBackendName(node *api.ServiceEntry) string {
|
||||
return strings.ToLower(node.Service.Service)
|
||||
}
|
||||
|
||||
func getBackendAddress(node *api.ServiceEntry) string {
|
||||
if node.Service.Address != "" {
|
||||
return node.Service.Address
|
||||
}
|
||||
return node.Node.Address
|
||||
}
|
||||
|
||||
func getServerName(node *api.ServiceEntry, index int) string {
|
||||
serviceName := node.Service.Service + node.Service.Address + strconv.Itoa(node.Service.Port)
|
||||
// TODO sort tags ?
|
||||
serviceName += strings.Join(node.Service.Tags, "")
|
||||
|
||||
hash := sha1.New()
|
||||
_, err := hash.Write([]byte(serviceName))
|
||||
if err != nil {
|
||||
// Impossible case
|
||||
log.Error(err)
|
||||
} else {
|
||||
serviceName = base64.URLEncoding.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
// unique int at the end
|
||||
return provider.Normalize(node.Service.Service + "-" + strconv.Itoa(index) + "-" + serviceName)
|
||||
}
|
||||
|
||||
func (p *Provider) getWeight(tags []string) int {
|
||||
weight := p.getIntAttribute(label.SuffixWeight, tags, label.DefaultWeightInt)
|
||||
|
||||
// Deprecated
|
||||
deprecatedWeightTag := "backend." + label.SuffixWeight
|
||||
if p.hasAttribute(deprecatedWeightTag, tags) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.",
|
||||
p.getPrefixedName(deprecatedWeightTag), p.getPrefixedName(label.SuffixWeight))
|
||||
|
||||
weight = p.getIntAttribute(deprecatedWeightTag, tags, label.DefaultWeightInt)
|
||||
}
|
||||
|
||||
return weight
|
||||
}
|
||||
|
||||
// Base functions
|
||||
|
||||
func (p *Provider) hasAttribute(name string, tags []string) bool {
|
||||
return hasTag(p.getPrefixedName(name), tags)
|
||||
}
|
||||
|
||||
func (p *Provider) getAttribute(name string, tags []string, defaultValue string) string {
|
||||
return getTag(p.getPrefixedName(name), tags, defaultValue)
|
||||
}
|
||||
|
||||
func (p *Provider) getPrefixedName(name string) string {
|
||||
if len(p.Prefix) > 0 && len(name) > 0 {
|
||||
return p.Prefix + "." + name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func hasTag(name string, tags []string) bool {
|
||||
lowerName := strings.ToLower(name)
|
||||
|
||||
for _, tag := range tags {
|
||||
lowerTag := strings.ToLower(tag)
|
||||
|
||||
// Given the nature of Consul tags, which could be either singular markers, or key=value pairs
|
||||
if strings.HasPrefix(lowerTag, lowerName+"=") || lowerTag == lowerName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasTagPrefix(name string, tags []string) bool {
|
||||
lowerName := strings.ToLower(name)
|
||||
|
||||
for _, tag := range tags {
|
||||
lowerTag := strings.ToLower(tag)
|
||||
|
||||
if strings.HasPrefix(lowerTag, lowerName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getTag(name string, tags []string, defaultValue string) string {
|
||||
lowerName := strings.ToLower(name)
|
||||
|
||||
for _, tag := range tags {
|
||||
lowerTag := strings.ToLower(tag)
|
||||
|
||||
// Given the nature of Consul tags, which could be either singular markers, or key=value pairs
|
||||
if strings.HasPrefix(lowerTag, lowerName+"=") || lowerTag == lowerName {
|
||||
// In case, where a tag might be a key=value, try to split it by the first '='
|
||||
kv := strings.SplitN(tag, "=", 2)
|
||||
|
||||
// If the returned result is a key=value pair, return the 'value' component
|
||||
if len(kv) == 2 {
|
||||
return kv[1]
|
||||
}
|
||||
// If the returned result is a singular marker, return the 'key' component
|
||||
return kv[0]
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue