fix: backend name for Stateful services. (Service Fabric)

This commit is contained in:
Ludovic Fernandez 2017-12-15 01:22:03 +01:00 committed by Traefiker
parent 350d61b4a6
commit 799136a714
6 changed files with 174 additions and 151 deletions

8
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 798765d68f54df534f091b4963f76f3e26d8dac2cb444368c2fa1c572e3c1ea6 hash: 03cd7f5ecab087e73cc395cb61b58b82cefef55969aa368f93fc17095b92815f
updated: 2017-11-30T10:34:41.246378337+01:00 updated: 2017-12-15T10:34:41.246378337+01:00
imports: imports:
- name: cloud.google.com/go - name: cloud.google.com/go
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
@ -94,7 +94,7 @@ imports:
- name: github.com/containous/staert - name: github.com/containous/staert
version: af517d5b70db9c4b0505e0144fcc62b054057d2a version: af517d5b70db9c4b0505e0144fcc62b054057d2a
- name: github.com/containous/traefik-extra-service-fabric - name: github.com/containous/traefik-extra-service-fabric
version: 8076098dbfe814cba9e895ecbd896f1896b6b2d5 version: c01c1ef60ed612c5e42c1ceae0c6f92e67619cc3
- name: github.com/coreos/bbolt - name: github.com/coreos/bbolt
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423 version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
- name: github.com/coreos/etcd - name: github.com/coreos/etcd
@ -348,7 +348,7 @@ imports:
subpackages: subpackages:
- lib - lib
- name: github.com/jjcollinge/servicefabric - name: github.com/jjcollinge/servicefabric
version: 93a44e59fc887cda489913c6fc5bda834989f3bd version: 8026935326c842b71dee8e2329c1fda41a7a92f4
- name: github.com/jmespath/go-jmespath - name: github.com/jmespath/go-jmespath
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
- name: github.com/jonboulle/clockwork - name: github.com/jonboulle/clockwork

View file

@ -12,7 +12,7 @@ import:
- package: github.com/cenk/backoff - package: github.com/cenk/backoff
- package: github.com/containous/flaeg - package: github.com/containous/flaeg
- package: github.com/containous/traefik-extra-service-fabric - package: github.com/containous/traefik-extra-service-fabric
version: v1.0.1 version: v1.0.4
- package: github.com/vulcand/oxy - package: github.com/vulcand/oxy
version: 7b6e758ab449705195df638765c4ca472248908a version: 7b6e758ab449705195df638765c4ca472248908a
repo: https://github.com/containous/oxy.git repo: https://github.com/containous/oxy.git

View file

@ -1,23 +1,59 @@
package servicefabric package servicefabric
import "strings" import (
"strconv"
"strings"
)
func hasServiceLabel(service ServiceItemExtended, key string) bool { func getFuncBoolLabel(labelName string, defaultValue bool) func(service ServiceItemExtended) bool {
_, exists := service.Labels[key]
return exists
}
func getFuncBoolLabel(labelName string) func(service ServiceItemExtended) bool {
return func(service ServiceItemExtended) bool { return func(service ServiceItemExtended) bool {
return getBoolLabel(service, labelName) return getBoolValue(service.Labels, labelName, defaultValue)
} }
} }
func getBoolLabel(service ServiceItemExtended, labelName string) bool { func getFuncServiceStringLabel(service ServiceItemExtended, labelName string, defaultValue string) string {
value, exists := service.Labels[labelName] return getStringValue(service.Labels, labelName, defaultValue)
return exists && strings.EqualFold(strings.TrimSpace(value), "true")
} }
func getServiceLabelValue(service ServiceItemExtended, key string) string { func hasFuncService(service ServiceItemExtended, labelName string) bool {
return service.Labels[key] return hasLabel(service.Labels, labelName)
}
func getServiceLabelsWithPrefix(service ServiceItemExtended, prefix string) map[string]string {
results := make(map[string]string)
for k, v := range service.Labels {
if strings.HasPrefix(k, prefix) {
results[k] = v
}
}
return results
}
// must be replace by label.Has()
// Deprecated
func hasLabel(labels map[string]string, labelName string) bool {
value, ok := labels[labelName]
return ok && len(value) > 0
}
// must be replace by label.GetStringValue()
// Deprecated
func getStringValue(labels map[string]string, labelName string, defaultValue string) string {
if value, ok := labels[labelName]; ok && len(value) > 0 {
return value
}
return defaultValue
}
// must be replace by label.GetBoolValue()
// Deprecated
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
} }

View file

@ -69,34 +69,7 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po
log.Info("Checking service fabric config") log.Info("Checking service fabric config")
} }
services, err := getClusterServices(sfClient) configuration, err := p.buildConfiguration(sfClient)
if err != nil {
return err
}
templateObjects := struct {
Services []ServiceItemExtended
}{
services,
}
var sfFuncMap = template.FuncMap{
"isPrimary": isPrimary,
"getDefaultEndpoint": p.getDefaultEndpoint,
"getNamedEndpoint": p.getNamedEndpoint,
"getApplicationParameter": p.getApplicationParameter,
"doesAppParamContain": p.doesAppParamContain,
"hasServiceLabel": hasServiceLabel,
"getServiceLabelValue": getServiceLabelValue,
"getServiceLabelValueWithDefault": getServiceLabelValueWithDefault,
"getServiceLabelsWithPrefix": getServiceLabelsWithPrefix,
"getServicesWithLabelValueMap": getServicesWithLabelValueMap,
"getServicesWithLabelValue": getServicesWithLabelValue,
"isExposed": getFuncBoolLabel("expose"),
}
configuration, err := p.GetConfiguration(tmpl, sfFuncMap, templateObjects)
if err != nil { if err != nil {
return err return err
} }
@ -120,24 +93,39 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po
return nil return nil
} }
func (p Provider) doesAppParamContain(app sf.ApplicationItem, key, shouldContain string) bool { func (p *Provider) buildConfiguration(sfClient sfClient) (*types.Configuration, error) {
value := p.getApplicationParameter(app, key) var sfFuncMap = template.FuncMap{
return strings.Contains(value, shouldContain) "getServices": getServices,
} "hasLabel": hasFuncService,
"getLabelValue": getFuncServiceStringLabel,
func (p Provider) getApplicationParameter(app sf.ApplicationItem, key string) string { "getLabelsWithPrefix": getServiceLabelsWithPrefix,
for _, param := range app.Parameters { "isPrimary": isPrimary,
if param.Key == key { "isExposed": getFuncBoolLabel("expose", false),
return param.Value "getBackendName": getBackendName,
} "getDefaultEndpoint": getDefaultEndpoint,
"getNamedEndpoint": getNamedEndpoint, // FIXME unused
"getApplicationParameter": getApplicationParameter, // FIXME unused
"doesAppParamContain": doesAppParamContain, // FIXME unused
"filterServicesByLabelValue": filterServicesByLabelValue, // FIXME unused
} }
log.Errorf("Parameter %s doesn't exist in app %s", key, app.Name)
return "" services, err := getClusterServices(sfClient)
if err != nil {
return nil, err
}
templateObjects := struct {
Services []ServiceItemExtended
}{
Services: services,
}
return p.GetConfiguration(tmpl, sfFuncMap, templateObjects)
} }
func (p Provider) getDefaultEndpoint(instance replicaInstance) string { func getDefaultEndpoint(instance replicaInstance) string {
id, data := instance.GetReplicaData() id, data := instance.GetReplicaData()
endpoint, err := getDefaultEndpoint(data.Address) endpoint, err := getReplicaDefaultEndpoint(data)
if err != nil { if err != nil {
log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address) log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address)
return "" return ""
@ -145,16 +133,64 @@ func (p Provider) getDefaultEndpoint(instance replicaInstance) string {
return endpoint return endpoint
} }
func (p Provider) getNamedEndpoint(instance replicaInstance, endpointName string) string { func getReplicaDefaultEndpoint(replicaData *sf.ReplicaItemBase) (string, error) {
id, data := instance.GetReplicaData() endpoints, err := decodeEndpointData(replicaData.Address)
endpoint, err := getNamedEndpoint(data.Address, endpointName)
if err != nil { if err != nil {
log.Warnf("No names endpoint of %s for replica %s in endpointData: %s", endpointName, id, data.Address) return "", err
}
var defaultHTTPEndpoint string
for _, v := range endpoints {
if strings.Contains(v, "http") {
defaultHTTPEndpoint = v
break
}
}
if len(defaultHTTPEndpoint) == 0 {
return "", errors.New("no default endpoint found")
}
return defaultHTTPEndpoint, nil
}
func getNamedEndpoint(instance replicaInstance, endpointName string) string {
id, data := instance.GetReplicaData()
endpoint, err := getReplicaNamedEndpoint(data, endpointName)
if err != nil {
log.Warnf("No names endpoint of %s for replica %s in endpointData: %s. Error: %v", endpointName, id, data.Address, err)
return "" return ""
} }
return endpoint return endpoint
} }
func getReplicaNamedEndpoint(replicaData *sf.ReplicaItemBase, endpointName string) (string, error) {
endpoints, err := decodeEndpointData(replicaData.Address)
if err != nil {
return "", err
}
endpoint, exists := endpoints[endpointName]
if !exists {
return "", errors.New("endpoint doesn't exist")
}
return endpoint, nil
}
func doesAppParamContain(app sf.ApplicationItem, key, shouldContain string) bool {
value := getApplicationParameter(app, key)
return strings.Contains(value, shouldContain)
}
func getApplicationParameter(app sf.ApplicationItem, key string) string {
for _, param := range app.Parameters {
if param.Key == key {
return param.Value
}
}
log.Errorf("Parameter %s doesn't exist in app %s", key, app.Name)
return ""
}
func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) { func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) {
apps, err := sfClient.GetApplications() apps, err := sfClient.GetApplications()
if err != nil { if err != nil {
@ -236,7 +272,7 @@ func getValidInstances(sfClient sfClient, app sf.ApplicationItem, service sf.Ser
return validInstances return validInstances
} }
func getServicesWithLabelValueMap(services []ServiceItemExtended, key string) map[string][]ServiceItemExtended { func getServices(services []ServiceItemExtended, key string) map[string][]ServiceItemExtended {
result := map[string][]ServiceItemExtended{} result := map[string][]ServiceItemExtended{}
for _, service := range services { for _, service := range services {
if value, exists := service.Labels[key]; exists { if value, exists := service.Labels[key]; exists {
@ -250,7 +286,7 @@ func getServicesWithLabelValueMap(services []ServiceItemExtended, key string) ma
return result return result
} }
func getServicesWithLabelValue(services []ServiceItemExtended, key, expectedValue string) []ServiceItemExtended { func filterServicesByLabelValue(services []ServiceItemExtended, key, expectedValue string) []ServiceItemExtended {
var srvWithLabel []ServiceItemExtended var srvWithLabel []ServiceItemExtended
for _, service := range services { for _, service := range services {
value, exists := service.Labels[key] value, exists := service.Labels[key]
@ -261,25 +297,6 @@ func getServicesWithLabelValue(services []ServiceItemExtended, key, expectedValu
return srvWithLabel return srvWithLabel
} }
func getServiceLabelValueWithDefault(service ServiceItemExtended, key, defaultValue string) string {
value, exists := service.Labels[key]
if !exists {
return defaultValue
}
return value
}
func getServiceLabelsWithPrefix(service ServiceItemExtended, prefix string) map[string]string {
results := make(map[string]string)
for k, v := range service.Labels {
if strings.HasPrefix(k, prefix) {
results[k] = v
}
}
return results
}
func isPrimary(instance replicaInstance) bool { func isPrimary(instance replicaInstance) bool {
_, data := instance.GetReplicaData() _, data := instance.GetReplicaData()
return data.ReplicaRole == "Primary" return data.ReplicaRole == "Primary"
@ -290,7 +307,7 @@ func isHealthy(instanceData *sf.ReplicaItemBase) bool {
} }
func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool { func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool {
_, err := getDefaultEndpoint(instanceData.Address) _, err := getReplicaDefaultEndpoint(instanceData)
return err == nil return err == nil
} }
@ -314,37 +331,6 @@ func decodeEndpointData(endpointData string) (map[string]string, error) {
return endpoints, nil return endpoints, nil
} }
func getDefaultEndpoint(endpointData string) (string, error) { func getBackendName(service ServiceItemExtended, partition PartitionItemExtended) string {
endpoints, err := decodeEndpointData(endpointData) return provider.Normalize(service.Name + partition.PartitionInformation.ID)
if err != nil {
return "", err
}
var defaultHTTPEndpointExists bool
var defaultHTTPEndpoint string
for _, v := range endpoints {
if strings.Contains(v, "http") {
defaultHTTPEndpoint = v
defaultHTTPEndpointExists = true
break
}
}
if !defaultHTTPEndpointExists {
return "", errors.New("no default endpoint found")
}
return defaultHTTPEndpoint, nil
}
func getNamedEndpoint(endpointData string, endpointName string) (string, error) {
endpoints, err := decodeEndpointData(endpointData)
if err != nil {
return "", err
}
endpoint, exists := endpoints[endpointName]
if !exists {
return "", errors.New("endpoint doesn't exist")
}
return endpoint, nil
} }

View file

@ -1,8 +1,8 @@
package servicefabric package servicefabric
const tmpl = ` const tmpl = `
{{$groupedServiceMap := getServices .Services "backend.group.name"}}
[backends] [backends]
{{$groupedServiceMap := getServicesWithLabelValueMap .Services "backend.group.name"}}
{{range $aggName, $aggServices := $groupedServiceMap }} {{range $aggName, $aggServices := $groupedServiceMap }}
[backends."{{$aggName}}"] [backends."{{$aggName}}"]
{{range $service := $aggServices}} {{range $service := $aggServices}}
@ -10,7 +10,7 @@ const tmpl = `
{{range $instance := $partition.Instances}} {{range $instance := $partition.Instances}}
[backends."{{$aggName}}".servers."{{$service.ID}}-{{$instance.ID}}"] [backends."{{$aggName}}".servers."{{$service.ID}}-{{$instance.ID}}"]
url = "{{getDefaultEndpoint $instance}}" url = "{{getDefaultEndpoint $instance}}"
weight = {{getServiceLabelValueWithDefault $service "backend.group.weight" "1"}} weight = {{getLabelValue $service "backend.group.weight" "1"}}
{{end}} {{end}}
{{end}} {{end}}
{{end}} {{end}}
@ -20,45 +20,45 @@ const tmpl = `
{{if eq $partition.ServiceKind "Stateless"}} {{if eq $partition.ServiceKind "Stateless"}}
[backends."{{$service.Name}}"] [backends."{{$service.Name}}"]
[backends."{{$service.Name}}".LoadBalancer] [backends."{{$service.Name}}".LoadBalancer]
{{if hasServiceLabel $service "backend.loadbalancer.method"}} {{if hasLabel $service "backend.loadbalancer.method"}}
method = "{{getServiceLabelValue $service "backend.loadbalancer.method" }}" method = "{{getLabelValue $service "backend.loadbalancer.method" "" }}"
{{else}} {{else}}
method = "drr" method = "drr"
{{end}} {{end}}
{{if hasServiceLabel $service "backend.healthcheck"}} {{if hasLabel $service "backend.healthcheck"}}
[backends."{{$service.Name}}".healthcheck] [backends."{{$service.Name}}".healthcheck]
path = "{{getServiceLabelValue $service "backend.healthcheck"}}" path = "{{getLabelValue $service "backend.healthcheck" ""}}"
interval = "{{getServiceLabelValueWithDefault $service "backend.healthcheck.interval" "10s"}}" interval = "{{getLabelValue $service "backend.healthcheck.interval" "10s"}}"
{{end}} {{end}}
{{if hasServiceLabel $service "backend.loadbalancer.stickiness"}} {{if hasLabel $service "backend.loadbalancer.stickiness"}}
[backends."{{$service.Name}}".LoadBalancer.stickiness] [backends."{{$service.Name}}".LoadBalancer.stickiness]
{{end}} {{end}}
{{if hasServiceLabel $service "backend.circuitbreaker"}} {{if hasLabel $service "backend.circuitbreaker"}}
[backends."{{$service.Name}}".circuitbreaker] [backends."{{$service.Name}}".circuitbreaker]
expression = "{{getServiceLabelValue $service "backend.circuitbreaker"}}" expression = "{{getLabelValue $service "backend.circuitbreaker" ""}}"
{{end}} {{end}}
{{if hasServiceLabel $service "backend.maxconn.amount"}} {{if hasLabel $service "backend.maxconn.amount"}}
[backends."{{$service.Name}}".maxconn] [backends."{{$service.Name}}".maxconn]
amount = {{getServiceLabelValue $service "backend.maxconn.amount"}} amount = {{getLabelValue $service "backend.maxconn.amount" ""}}
{{if hasServiceLabel $service "backend.maxconn.extractorfunc"}} {{if hasLabel $service "backend.maxconn.extractorfunc"}}
extractorfunc = "{{getServiceLabelValue $service "backend.maxconn.extractorfunc"}}" extractorfunc = "{{getLabelValue $service "backend.maxconn.extractorfunc" ""}}"
{{end}} {{end}}
{{end}} {{end}}
{{range $instance := $partition.Instances}} {{range $instance := $partition.Instances}}
[backends."{{$service.Name}}".servers."{{$instance.ID}}"] [backends."{{$service.Name}}".servers."{{$instance.ID}}"]
url = "{{getDefaultEndpoint $instance}}" url = "{{getDefaultEndpoint $instance}}"
weight = {{getServiceLabelValueWithDefault $service "backend.weight" "1"}} weight = {{getLabelValue $service "backend.weight" "1"}}
{{end}} {{end}}
{{else if eq $partition.ServiceKind "Stateful"}} {{else if eq $partition.ServiceKind "Stateful"}}
{{range $replica := $partition.Replicas}} {{range $replica := $partition.Replicas}}
{{if isPrimary $replica}} {{if isPrimary $replica}}
{{$backendName := (print $service.Name $partition.PartitionInformation.ID)}} {{$backendName := getBackendName $service.Name $partition}}
[backends."{{$backendName}}".servers."{{$replica.ID}}"] [backends."{{$backendName}}".servers."{{$replica.ID}}"]
url = "{{getDefaultEndpoint $replica}}" url = "{{getDefaultEndpoint $replica}}"
weight = 1 weight = 1
@ -81,11 +81,11 @@ const tmpl = `
[frontends."{{$groupName}}"] [frontends."{{$groupName}}"]
backend = "{{$groupName}}" backend = "{{$groupName}}"
{{if hasServiceLabel $service "frontend.priority"}} {{if hasLabel $service "frontend.priority"}}
priority = 100 priority = 100
{{end}} {{end}}
{{range $key, $value := getServiceLabelsWithPrefix $service "frontend.rule"}} {{range $key, $value := getLabelsWithPrefix $service "frontend.rule"}}
[frontends."{{$groupName}}".routes."{{$key}}"] [frontends."{{$groupName}}".routes."{{$key}}"]
rule = "{{$value}}" rule = "{{$value}}"
{{end}} {{end}}
@ -97,27 +97,27 @@ const tmpl = `
[frontends."{{$service.Name}}"] [frontends."{{$service.Name}}"]
backend = "{{$service.Name}}" backend = "{{$service.Name}}"
{{if hasServiceLabel $service "frontend.passHostHeader"}} {{if hasLabel $service "frontend.passHostHeader"}}
passHostHeader = {{getServiceLabelValue $service "frontend.passHostHeader" }} passHostHeader = {{getLabelValue $service "frontend.passHostHeader" ""}}
{{end}} {{end}}
{{if hasServiceLabel $service "frontend.whitelistSourceRange"}} {{if hasLabel $service "frontend.whitelistSourceRange"}}
whitelistSourceRange = {{getServiceLabelValue $service "frontend.whitelistSourceRange" }} whitelistSourceRange = {{getLabelValue $service "frontend.whitelistSourceRange" ""}}
{{end}} {{end}}
{{if hasServiceLabel $service "frontend.priority"}} {{if hasLabel $service "frontend.priority"}}
priority = {{getServiceLabelValue $service "frontend.priority"}} priority = {{getLabelValue $service "frontend.priority" ""}}
{{end}} {{end}}
{{if hasServiceLabel $service "frontend.basicAuth"}} {{if hasLabel $service "frontend.basicAuth"}}
basicAuth = {{getServiceLabelValue $service "frontend.basicAuth"}} basicAuth = {{getLabelValue $service "frontend.basicAuth" ""}}
{{end}} {{end}}
{{if hasServiceLabel $service "frontend.entryPoints"}} {{if hasLabel $service "frontend.entryPoints"}}
entryPoints = {{getServiceLabelValue $service "frontend.entryPoints"}} entryPoints = {{getLabelValue $service "frontend.entryPoints" ""}}
{{end}} {{end}}
{{range $key, $value := getServiceLabelsWithPrefix $service "frontend.rule"}} {{range $key, $value := getLabelsWithPrefix $service "frontend.rule"}}
[frontends."{{$service.Name}}".routes."{{$key}}"] [frontends."{{$service.Name}}".routes."{{$key}}"]
rule = "{{$value}}" rule = "{{$value}}"
{{end}} {{end}}
@ -126,11 +126,11 @@ const tmpl = `
{{range $partition := $service.Partitions}} {{range $partition := $service.Partitions}}
{{$partitionId := $partition.PartitionInformation.ID}} {{$partitionId := $partition.PartitionInformation.ID}}
{{if hasServiceLabel $service "frontend.rule"}} {{if hasLabel $service "frontend.rule"}}
[frontends."{{$service.Name}}/{{$partitionId}}"] [frontends."{{$service.Name}}/{{$partitionId}}"]
backend = "{{$service.Name}}/{{$partitionId}}" backend = "{{getBackendName $service.Name $partition}}"
[frontends."{{$service.Name}}/{{$partitionId}}".routes.default] [frontends."{{$service.Name}}/{{$partitionId}}".routes.default]
rule = {{getServiceLabelValue $service "frontend.rule.partition.$partitionId"}} rule = {{getLabelValue $service "frontend.rule.partition.$partitionId" ""}}
{{end}} {{end}}
{{end}} {{end}}

View file

@ -12,6 +12,7 @@ import (
"strings" "strings"
) )
// DefaultAPIVersion is a default Service Fabric REST API version
const DefaultAPIVersion = "3.0" const DefaultAPIVersion = "3.0"
// Client for Service Fabric. // Client for Service Fabric.