From 0fa0c2256a288256854d2b8ac9085801f9805b29 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 22 Mar 2018 17:42:03 +0100 Subject: [PATCH] Update Service Fabric backend. --- Gopkg.lock | 9 +- Gopkg.toml | 2 +- docs/configuration/backends/servicefabric.md | 82 +++-- .../traefik-extra-service-fabric/labels.go | 59 ---- .../servicefabric.go | 165 +-------- .../servicefabric_config.go | 321 ++++++++++++++++++ .../servicefabric_labelfuncs.go | 63 ++++ .../servicefabric_tmpl.go | 293 ++++++++++------ .../traefik-extra-service-fabric/types.go | 11 +- .../jjcollinge/servicefabric/servicefabric.go | 24 ++ 10 files changed, 677 insertions(+), 352 deletions(-) delete mode 100644 vendor/github.com/containous/traefik-extra-service-fabric/labels.go create mode 100644 vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_config.go create mode 100644 vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_labelfuncs.go diff --git a/Gopkg.lock b/Gopkg.lock index db29dc206..49c972af5 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -247,8 +247,8 @@ [[projects]] name = "github.com/containous/traefik-extra-service-fabric" packages = ["."] - revision = "ca1fb57108293caad285b1c366b763f6c6ab71c9" - version = "v1.0.5" + revision = "a0b20089e99069884b060875fc015c13a23e7953" + version = "v1.1.0" [[projects]] name = "github.com/coreos/bbolt" @@ -745,10 +745,9 @@ version = "v1.3.7" [[projects]] - branch = "master" name = "github.com/jjcollinge/servicefabric" packages = ["."] - revision = "8026935326c842b71dee8e2329c1fda41a7a92f4" + revision = "8eebe170fa1ba25d3dfb928b3f86a7313b13b9fe" [[projects]] name = "github.com/jmespath/go-jmespath" @@ -1633,6 +1632,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "39b400d9928cce714a46cae410074fd2622a097a9e0faba76a7b21537fabd728" + inputs-digest = "ab328aeda9dbd2c4dc87061c25dbfbd151dbdc5946b6f512f676b39bebba8d8e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 19fdd8c5b..571a70995 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -66,7 +66,7 @@ [[constraint]] name = "github.com/containous/traefik-extra-service-fabric" - version = "1.0.5" + version = "1.1.0" [[constraint]] name = "github.com/coreos/go-systemd" diff --git a/docs/configuration/backends/servicefabric.md b/docs/configuration/backends/servicefabric.md index b87a92675..69310833f 100644 --- a/docs/configuration/backends/servicefabric.md +++ b/docs/configuration/backends/servicefabric.md @@ -69,10 +69,10 @@ Here is an example of an extension setting Træfik labels: ``` -#### Property Manager +#### Property Manager Set Labels with the property manager API to overwrite and add labels, while your service is running. -Here is an example of adding a frontend rule using the property manager API. +Here is an example of adding a frontend rule using the property manager API. ```shell curl -X PUT \ @@ -92,23 +92,63 @@ curl -X PUT \ ## Available Labels -Labels, set through extensions or the property manager, can be used on services to override default behaviour. +Labels, set through extensions or the property manager, can be used on services to override default behavior. -| Label | Description | -|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | -| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.weight=10` | Assign this weight to the container | -| `traefik.expose=true` | Expose this service using træfik | -| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. | -| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | -| `traefik.frontend.priority=10` | Override default frontend priority | -| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` | -| `traefik.frontend.auth.basic=EXPR` | Set basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | -| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik | -| `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group | +| Label | Description | +|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.enable=false` | Disable this container in Træfik | +| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik | +| `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group | +| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | +| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. | +| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | +| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) | +| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.backend.weight=10` | Assign this weight to the container | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | +| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Override default frontend priority | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. | +| `traefik.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | + +### Custom Headers + +| Label | Description | +|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.
Format: HEADER:value||HEADER2:value2 | +| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.
Format: HEADER:value||HEADER2:value2 | + +### Security Headers + +| Label | Description | +|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.
Format: `Host1,Host2` | +| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.
Format: `HEADER1,HEADER2` | +| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. | +| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. | +| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. | +| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).
Format: HEADER:value||HEADER2:value2 | +| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. | +| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. | +| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. | +| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. | +| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. | +| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. | +| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. | +| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. | +| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. | +| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. | +| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. | +| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. | +| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.
When deploying to production, be sure to set this to false. | diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/labels.go b/vendor/github.com/containous/traefik-extra-service-fabric/labels.go deleted file mode 100644 index f2878d20a..000000000 --- a/vendor/github.com/containous/traefik-extra-service-fabric/labels.go +++ /dev/null @@ -1,59 +0,0 @@ -package servicefabric - -import ( - "strconv" - "strings" -) - -func getFuncBoolLabel(labelName string, defaultValue bool) func(service ServiceItemExtended) bool { - return func(service ServiceItemExtended) bool { - return getBoolValue(service.Labels, labelName, defaultValue) - } -} - -func getFuncServiceStringLabel(service ServiceItemExtended, labelName string, defaultValue string) string { - return getStringValue(service.Labels, labelName, defaultValue) -} - -func hasFuncService(service ServiceItemExtended, labelName string) bool { - 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 -} diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go index c23153487..b86aa32a3 100644 --- a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go +++ b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go @@ -1,17 +1,14 @@ package servicefabric import ( - "encoding/json" - "errors" "net/http" - "strings" - "text/template" "time" "github.com/cenk/backoff" "github.com/containous/traefik/job" "github.com/containous/traefik/log" "github.com/containous/traefik/provider" + "github.com/containous/traefik/provider/label" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" sf "github.com/jjcollinge/servicefabric" @@ -19,7 +16,7 @@ import ( var _ provider.Provider = (*Provider)(nil) -const traefikLabelPrefix = "traefik" +const traefikServiceFabricExtensionKey = "Traefik" // Provider holds for configuration for the provider type Provider struct { @@ -93,104 +90,6 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po return nil } -func (p *Provider) buildConfiguration(sfClient sfClient) (*types.Configuration, error) { - var sfFuncMap = template.FuncMap{ - "getServices": getServices, - "hasLabel": hasFuncService, - "getLabelValue": getFuncServiceStringLabel, - "getLabelsWithPrefix": getServiceLabelsWithPrefix, - "isPrimary": isPrimary, - "isExposed": getFuncBoolLabel("expose", false), - "getBackendName": getBackendName, - "getDefaultEndpoint": getDefaultEndpoint, - "getNamedEndpoint": getNamedEndpoint, // FIXME unused - "getApplicationParameter": getApplicationParameter, // FIXME unused - "doesAppParamContain": doesAppParamContain, // FIXME unused - "filterServicesByLabelValue": filterServicesByLabelValue, // FIXME unused - } - - services, err := getClusterServices(sfClient) - if err != nil { - return nil, err - } - - templateObjects := struct { - Services []ServiceItemExtended - }{ - Services: services, - } - - return p.GetConfiguration(tmpl, sfFuncMap, templateObjects) -} - -func getDefaultEndpoint(instance replicaInstance) string { - id, data := instance.GetReplicaData() - endpoint, err := getReplicaDefaultEndpoint(data) - if err != nil { - log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address) - return "" - } - return endpoint -} - -func getReplicaDefaultEndpoint(replicaData *sf.ReplicaItemBase) (string, error) { - endpoints, err := decodeEndpointData(replicaData.Address) - if err != nil { - 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 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) { apps, err := sfClient.GetApplications() if err != nil { @@ -210,7 +109,7 @@ func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) { Application: app, } - if labels, err := sfClient.GetServiceLabels(&service, &app, traefikLabelPrefix); err != nil { + if labels, err := getLabels(sfClient, &service, &app); err != nil { log.Error(err) } else { item.Labels = labels @@ -222,9 +121,9 @@ func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) { for _, partition := range partitions.Items { partitionExt := PartitionItemExtended{PartitionItem: partition} - if partition.ServiceKind == "Stateful" { + if isStateful(item) { partitionExt.Replicas = getValidReplicas(sfClient, app, service, partition) - } else if partition.ServiceKind == "Stateless" { + } else if isStateless(item) { partitionExt.Instances = getValidInstances(sfClient, app, service, partition) } else { log.Errorf("Unsupported service kind %s in service %s", partition.ServiceKind, service.Name) @@ -272,31 +171,6 @@ func getValidInstances(sfClient sfClient, app sf.ApplicationItem, service sf.Ser return validInstances } -func getServices(services []ServiceItemExtended, key string) map[string][]ServiceItemExtended { - result := map[string][]ServiceItemExtended{} - for _, service := range services { - if value, exists := service.Labels[key]; exists { - if matchingServices, hasKeyAlready := result[value]; hasKeyAlready { - result[value] = append(matchingServices, service) - } else { - result[value] = []ServiceItemExtended{service} - } - } - } - return result -} - -func filterServicesByLabelValue(services []ServiceItemExtended, key, expectedValue string) []ServiceItemExtended { - var srvWithLabel []ServiceItemExtended - for _, service := range services { - value, exists := service.Labels[key] - if exists && value == expectedValue { - srvWithLabel = append(srvWithLabel, service) - } - } - return srvWithLabel -} - func isPrimary(instance replicaInstance) bool { _, data := instance.GetReplicaData() return data.ReplicaRole == "Primary" @@ -311,26 +185,21 @@ func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool { return err == nil } -func decodeEndpointData(endpointData string) (map[string]string, error) { - var endpointsMap map[string]map[string]string - - if endpointData == "" { - return nil, errors.New("endpoint data is empty") - } - - err := json.Unmarshal([]byte(endpointData), &endpointsMap) +// Return a set of labels from the Extension and Property manager +// Allow Extension labels to disable importing labels from the property manager. +func getLabels(sfClient sfClient, service *sf.ServiceItem, app *sf.ApplicationItem) (map[string]string, error) { + labels, err := sfClient.GetServiceExtensionMap(service, app, traefikServiceFabricExtensionKey) if err != nil { + log.Errorf("Error retrieving serviceExtensionMap: %v", err) return nil, err } - endpoints, endpointsExist := endpointsMap["Endpoints"] - if !endpointsExist { - return nil, errors.New("endpoint doesn't exist in endpoint data") + if label.GetBoolValue(labels, traefikSFEnableLabelOverrides, traefikSFEnableLabelOverridesDefault) { + if exists, properties, err := sfClient.GetProperties(service.ID); err == nil && exists { + for key, value := range properties { + labels[key] = value + } + } } - - return endpoints, nil -} - -func getBackendName(service ServiceItemExtended, partition PartitionItemExtended) string { - return provider.Normalize(service.Name + partition.PartitionInformation.ID) + return labels, nil } diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_config.go b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_config.go new file mode 100644 index 000000000..e05ec4ea7 --- /dev/null +++ b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_config.go @@ -0,0 +1,321 @@ +package servicefabric + +import ( + "encoding/json" + "errors" + "math" + "strings" + "text/template" + + "github.com/containous/traefik/log" + "github.com/containous/traefik/provider" + "github.com/containous/traefik/provider/label" + "github.com/containous/traefik/types" + sf "github.com/jjcollinge/servicefabric" +) + +func (p *Provider) buildConfiguration(sfClient sfClient) (*types.Configuration, error) { + var sfFuncMap = template.FuncMap{ + + // Services + "getServices": getServices, + "hasLabel": hasService, + "getLabelValue": getServiceStringLabel, + "getLabelsWithPrefix": getServiceLabelsWithPrefix, + "isPrimary": isPrimary, + "isStateful": isStateful, + "isStateless": isStateless, + "isEnabled": getFuncBoolLabel(label.TraefikEnable, false), + "getBackendName": getBackendName, + "getDefaultEndpoint": getDefaultEndpoint, + "getNamedEndpoint": getNamedEndpoint, // FIXME unused + "getApplicationParameter": getApplicationParameter, // FIXME unused + "doesAppParamContain": doesAppParamContain, // FIXME unused + "filterServicesByLabelValue": filterServicesByLabelValue, // FIXME unused + + // Backend functions + "getWeight": getFuncServiceStringLabel(label.TraefikWeight, label.DefaultWeight), + "getProtocol": getFuncServiceStringLabel(label.TraefikProtocol, label.DefaultProtocol), + "getMaxConn": getMaxConn, + "getHealthCheck": getHealthCheck, + "getCircuitBreaker": getCircuitBreaker, + "getLoadBalancer": getLoadBalancer, + + // Frontend Functions + "getPriority": getFuncServiceStringLabel(label.TraefikFrontendPriority, label.DefaultFrontendPriority), + "getPassHostHeader": getFuncServiceStringLabel(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), + "getPassTLSCert": getFuncBoolLabel(label.TraefikFrontendPassTLSCert, false), + "getEntryPoints": getFuncServiceSliceStringLabel(label.TraefikFrontendEntryPoints), + "getBasicAuth": getFuncServiceSliceStringLabel(label.TraefikFrontendAuthBasic), + "getWhitelistSourceRange": getFuncServiceSliceStringLabel(label.TraefikFrontendWhitelistSourceRange), + "getFrontendRules": getFuncServiceLabelWithPrefix(label.TraefikFrontendRule), + + "getHeaders": getHeaders, + "getRedirect": getRedirect, + + // SF Service Grouping + "getGroupedServices": getFuncServicesGroupedByLabel(traefikSFGroupName), + "getGroupedWeight": getFuncServiceStringLabel(traefikSFGroupWeight, "1"), + } + + services, err := getClusterServices(sfClient) + if err != nil { + return nil, err + } + + templateObjects := struct { + Services []ServiceItemExtended + }{ + Services: services, + } + + return p.GetConfiguration(tmpl, sfFuncMap, templateObjects) +} + +func isStateful(service ServiceItemExtended) bool { + return service.ServiceKind == "Stateful" +} + +func isStateless(service ServiceItemExtended) bool { + return service.ServiceKind == "Stateless" +} + +func getBackendName(service ServiceItemExtended, partition PartitionItemExtended) string { + return provider.Normalize(service.Name + partition.PartitionInformation.ID) +} + +func getDefaultEndpoint(instance replicaInstance) string { + id, data := instance.GetReplicaData() + endpoint, err := getReplicaDefaultEndpoint(data) + if err != nil { + log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address) + return "" + } + return endpoint +} + +func getReplicaDefaultEndpoint(replicaData *sf.ReplicaItemBase) (string, error) { + endpoints, err := decodeEndpointData(replicaData.Address) + if err != nil { + 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 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 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 getServices(services []ServiceItemExtended, key string) map[string][]ServiceItemExtended { + result := map[string][]ServiceItemExtended{} + for _, service := range services { + if value, exists := service.Labels[key]; exists { + if matchingServices, hasKeyAlready := result[value]; hasKeyAlready { + result[value] = append(matchingServices, service) + } else { + result[value] = []ServiceItemExtended{service} + } + } + } + return result +} + +func doesAppParamContain(app sf.ApplicationItem, key, shouldContain string) bool { + value := getApplicationParameter(app, key) + return strings.Contains(value, shouldContain) +} + +func filterServicesByLabelValue(services []ServiceItemExtended, key, expectedValue string) []ServiceItemExtended { + var srvWithLabel []ServiceItemExtended + for _, service := range services { + value, exists := service.Labels[key] + if exists && value == expectedValue { + srvWithLabel = append(srvWithLabel, service) + } + } + return srvWithLabel +} + +func decodeEndpointData(endpointData string) (map[string]string, error) { + var endpointsMap map[string]map[string]string + + if endpointData == "" { + return nil, errors.New("endpoint data is empty") + } + + err := json.Unmarshal([]byte(endpointData), &endpointsMap) + if err != nil { + return nil, err + } + + endpoints, endpointsExist := endpointsMap["Endpoints"] + if !endpointsExist { + return nil, errors.New("endpoint doesn't exist in endpoint data") + } + + return endpoints, nil +} + +func getHeaders(service ServiceItemExtended) *types.Headers { + headers := &types.Headers{ + CustomRequestHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendRequestHeaders), + CustomResponseHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendResponseHeaders), + SSLProxyHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendSSLProxyHeaders), + AllowedHosts: label.GetSliceStringValue(service.Labels, label.TraefikFrontendAllowedHosts), + HostsProxyHeaders: label.GetSliceStringValue(service.Labels, label.TraefikFrontendHostsProxyHeaders), + STSSeconds: label.GetInt64Value(service.Labels, label.TraefikFrontendSTSSeconds, 0), + SSLRedirect: label.GetBoolValue(service.Labels, label.TraefikFrontendSSLRedirect, false), + SSLTemporaryRedirect: label.GetBoolValue(service.Labels, label.TraefikFrontendSSLTemporaryRedirect, false), + STSIncludeSubdomains: label.GetBoolValue(service.Labels, label.TraefikFrontendSTSIncludeSubdomains, false), + STSPreload: label.GetBoolValue(service.Labels, label.TraefikFrontendSTSPreload, false), + ForceSTSHeader: label.GetBoolValue(service.Labels, label.TraefikFrontendForceSTSHeader, false), + FrameDeny: label.GetBoolValue(service.Labels, label.TraefikFrontendFrameDeny, false), + ContentTypeNosniff: label.GetBoolValue(service.Labels, label.TraefikFrontendContentTypeNosniff, false), + BrowserXSSFilter: label.GetBoolValue(service.Labels, label.TraefikFrontendBrowserXSSFilter, false), + IsDevelopment: label.GetBoolValue(service.Labels, label.TraefikFrontendIsDevelopment, false), + SSLHost: label.GetStringValue(service.Labels, label.TraefikFrontendSSLHost, ""), + CustomFrameOptionsValue: label.GetStringValue(service.Labels, label.TraefikFrontendCustomFrameOptionsValue, ""), + ContentSecurityPolicy: label.GetStringValue(service.Labels, label.TraefikFrontendContentSecurityPolicy, ""), + PublicKey: label.GetStringValue(service.Labels, label.TraefikFrontendPublicKey, ""), + ReferrerPolicy: label.GetStringValue(service.Labels, label.TraefikFrontendReferrerPolicy, ""), + CustomBrowserXSSValue: label.GetStringValue(service.Labels, label.TraefikFrontendCustomBrowserXSSValue, ""), + } + + if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() { + return nil + } + + return headers +} + +func getRedirect(service ServiceItemExtended) *types.Redirect { + permanent := label.GetBoolValue(service.Labels, label.TraefikFrontendRedirectPermanent, false) + + if label.Has(service.Labels, label.TraefikFrontendRedirectEntryPoint) { + return &types.Redirect{ + EntryPoint: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectEntryPoint, ""), + Permanent: permanent, + } + } + + if label.Has(service.Labels, label.TraefikFrontendRedirectRegex) && + label.Has(service.Labels, label.TraefikFrontendRedirectReplacement) { + return &types.Redirect{ + Regex: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectRegex, ""), + Replacement: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectReplacement, ""), + Permanent: permanent, + } + } + + return nil +} + +func getMaxConn(service ServiceItemExtended) *types.MaxConn { + amount := label.GetInt64Value(service.Labels, label.TraefikBackendMaxConnAmount, math.MinInt64) + extractorFunc := label.GetStringValue(service.Labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc) + + if amount == math.MinInt64 || len(extractorFunc) == 0 { + return nil + } + + return &types.MaxConn{ + Amount: amount, + ExtractorFunc: extractorFunc, + } +} + +func getHealthCheck(service ServiceItemExtended) *types.HealthCheck { + path := label.GetStringValue(service.Labels, label.TraefikBackendHealthCheckPath, "") + if len(path) == 0 { + return nil + } + + port := label.GetIntValue(service.Labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort) + interval := label.GetStringValue(service.Labels, label.TraefikBackendHealthCheckInterval, "") + + return &types.HealthCheck{ + Path: path, + Port: port, + Interval: interval, + } +} + +func getCircuitBreaker(service ServiceItemExtended) *types.CircuitBreaker { + circuitBreaker := label.GetStringValue(service.Labels, label.TraefikBackendCircuitBreakerExpression, "") + if len(circuitBreaker) == 0 { + return nil + } + return &types.CircuitBreaker{Expression: circuitBreaker} +} + +func getLoadBalancer(service ServiceItemExtended) *types.LoadBalancer { + if !label.HasPrefix(service.Labels, label.TraefikBackendLoadBalancer) { + return nil + } + + method := label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod) + + lb := &types.LoadBalancer{ + Method: method, + Sticky: getSticky(service), + } + + if label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerStickiness, false) { + cookieName := label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName) + lb.Stickiness = &types.Stickiness{CookieName: cookieName} + } + + return lb +} + +// TODO: Deprecated +// replaced by Stickiness +// Deprecated +func getSticky(service ServiceItemExtended) bool { + if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) { + log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness) + } + + return label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerSticky, false) +} diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_labelfuncs.go b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_labelfuncs.go new file mode 100644 index 000000000..14112e486 --- /dev/null +++ b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_labelfuncs.go @@ -0,0 +1,63 @@ +package servicefabric + +import ( + "strings" + + "github.com/containous/traefik/provider/label" +) + +// SF Specific Traefik Labels +const ( + traefikSFGroupName = "traefik.servicefabric.groupname" + traefikSFGroupWeight = "traefik.servicefabric.groupweight" + traefikSFEnableLabelOverrides = "traefik.servicefabric.enablelabeloverrides" + traefikSFEnableLabelOverridesDefault = true +) + +func getFuncBoolLabel(labelName string, defaultValue bool) func(service ServiceItemExtended) bool { + return func(service ServiceItemExtended) bool { + return label.GetBoolValue(service.Labels, labelName, defaultValue) + } +} + +func getServiceStringLabel(service ServiceItemExtended, labelName string, defaultValue string) string { + return label.GetStringValue(service.Labels, labelName, defaultValue) +} + +func getFuncServiceStringLabel(labelName string, defaultValue string) func(service ServiceItemExtended) string { + return func(service ServiceItemExtended) string { + return label.GetStringValue(service.Labels, labelName, defaultValue) + } +} + +func getFuncServiceSliceStringLabel(labelName string) func(service ServiceItemExtended) []string { + return func(service ServiceItemExtended) []string { + return label.GetSliceStringValue(service.Labels, labelName) + } +} + +func hasService(service ServiceItemExtended, labelName string) bool { + return label.Has(service.Labels, labelName) +} + +func getFuncServiceLabelWithPrefix(labelName string) func(service ServiceItemExtended) map[string]string { + return func(service ServiceItemExtended) map[string]string { + return getServiceLabelsWithPrefix(service, labelName) + } +} + +func getFuncServicesGroupedByLabel(labelName string) func(services []ServiceItemExtended) map[string][]ServiceItemExtended { + return func(services []ServiceItemExtended) map[string][]ServiceItemExtended { + return getServices(services, 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 +} diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go index 74be5033d..078d32fdb 100644 --- a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go +++ b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go @@ -1,140 +1,209 @@ package servicefabric const tmpl = ` -{{$groupedServiceMap := getServices .Services "backend.group.name"}} [backends] - {{range $aggName, $aggServices := $groupedServiceMap }} - [backends."{{$aggName}}"] - {{range $service := $aggServices}} - {{range $partition := $service.Partitions}} - {{range $instance := $partition.Instances}} - [backends."{{$aggName}}".servers."{{$service.ID}}-{{$instance.ID}}"] - url = "{{getDefaultEndpoint $instance}}" - weight = {{getLabelValue $service "backend.group.weight" "1"}} - {{end}} - {{end}} - {{end}} - {{end}} - {{range $service := .Services}} - {{range $partition := $service.Partitions}} - {{if eq $partition.ServiceKind "Stateless"}} - [backends."{{$service.Name}}"] - [backends."{{$service.Name}}".LoadBalancer] - {{if hasLabel $service "backend.loadbalancer.method"}} - method = "{{getLabelValue $service "backend.loadbalancer.method" "" }}" - {{else}} - method = "drr" +{{range $aggName, $aggServices := getGroupedServices .Services }} + [backends."{{ $aggName }}"] + {{range $service := $aggServices }} + {{range $partition := $service.Partitions }} + {{range $instance := $partition.Instances }} + [backends."{{ $aggName }}".servers."{{ $service.ID }}-{{ $instance.ID }}"] + url = "{{ getDefaultEndpoint $instance }}" + weight = {{ getGroupedWeight $service }} + {{end}} + {{end}} + {{end}} +{{end}} + +{{range $service := .Services }} + {{if isEnabled $service }} + {{range $partition := $service.Partitions }} + + {{if isStateless $service }} + + {{ $backendName := $service.Name }} + [backends."{{ $backendName }}"] + + {{ $circuitBreaker := getCircuitBreaker $service }} + {{if $circuitBreaker }} + [backends."{{ $backendName }}".circuitBreaker] + expression = "{{ $circuitBreaker.Expression }}" {{end}} - {{if hasLabel $service "backend.healthcheck"}} - [backends."{{$service.Name}}".healthcheck] - path = "{{getLabelValue $service "backend.healthcheck" ""}}" - interval = "{{getLabelValue $service "backend.healthcheck.interval" "10s"}}" + {{ $loadBalancer := getLoadBalancer $service }} + {{if $loadBalancer }} + [backends."{{ $backendName }}".loadBalancer] + method = "{{ $loadBalancer.Method }}" + sticky = {{ $loadBalancer.Sticky }} + {{if $loadBalancer.Stickiness }} + [backends."{{ $backendName }}".loadBalancer.stickiness] + cookieName = "{{ $loadBalancer.Stickiness.CookieName }}" + {{end}} {{end}} - {{if hasLabel $service "backend.loadbalancer.stickiness"}} - [backends."{{$service.Name}}".LoadBalancer.stickiness] + {{ $maxConn := getMaxConn $service }} + {{if $maxConn }} + [backends."{{ $backendName }}".maxConn] + extractorFunc = "{{ $maxConn.ExtractorFunc }}" + amount = {{ $maxConn.Amount }} {{end}} - {{if hasLabel $service "backend.circuitbreaker"}} - [backends."{{$service.Name}}".circuitbreaker] - expression = "{{getLabelValue $service "backend.circuitbreaker" ""}}" - {{end}} - - {{if hasLabel $service "backend.maxconn.amount"}} - [backends."{{$service.Name}}".maxconn] - amount = {{getLabelValue $service "backend.maxconn.amount" ""}} - {{if hasLabel $service "backend.maxconn.extractorfunc"}} - extractorfunc = "{{getLabelValue $service "backend.maxconn.extractorfunc" ""}}" - {{end}} + {{ $healthCheck := getHealthCheck $service }} + {{if $healthCheck }} + [backends."{{ $backendName }}".healthCheck] + path = "{{ $healthCheck.Path }}" + port = {{ $healthCheck.Port }} + interval = "{{ $healthCheck.Interval }}" {{end}} {{range $instance := $partition.Instances}} - [backends."{{$service.Name}}".servers."{{$instance.ID}}"] - url = "{{getDefaultEndpoint $instance}}" - weight = {{getLabelValue $service "backend.weight" "1"}} + [backends."{{ $service.Name }}".servers."{{ $instance.ID }}"] + url = "{{ getDefaultEndpoint $instance }}" + weight = {{ getLabelValue $service "backend.weight" "1" }} {{end}} - {{else if eq $partition.ServiceKind "Stateful"}} + + {{else if isStateful $service}} + {{range $replica := $partition.Replicas}} {{if isPrimary $replica}} - - {{$backendName := getBackendName $service.Name $partition}} - [backends."{{$backendName}}".servers."{{$replica.ID}}"] - url = "{{getDefaultEndpoint $replica}}" - weight = 1 + {{ $backendName := getBackendName $service $partition }} + [backends."{{ $backendName }}".servers."{{ $replica.ID }}"] + url = "{{ getDefaultEndpoint $replica }}" + weight = 1 [backends."{{$backendName}}".LoadBalancer] - method = "drr" - - [backends."{{$backendName}}".circuitbreaker] - expression = "NetworkErrorRatio() > 0.5" + method = "drr" {{end}} {{end}} - {{end}} - {{end}} -{{end}} - -[frontends] -{{range $groupName, $groupServices := $groupedServiceMap}} - {{$service := index $groupServices 0}} - [frontends."{{$groupName}}"] - backend = "{{$groupName}}" - - {{if hasLabel $service "frontend.priority"}} - priority = 100 - {{end}} - - {{range $key, $value := getLabelsWithPrefix $service "frontend.rule"}} - [frontends."{{$groupName}}".routes."{{$key}}"] - rule = "{{$value}}" - {{end}} -{{end}} -{{range $service := .Services}} - {{if isExposed $service}} - {{if eq $service.ServiceKind "Stateless"}} - - [frontends."{{$service.Name}}"] - backend = "{{$service.Name}}" - - {{if hasLabel $service "frontend.passHostHeader"}} - passHostHeader = {{getLabelValue $service "frontend.passHostHeader" ""}} - {{end}} - - {{if hasLabel $service "frontend.whitelistSourceRange"}} - whitelistSourceRange = {{getLabelValue $service "frontend.whitelistSourceRange" ""}} - {{end}} - - {{if hasLabel $service "frontend.priority"}} - priority = {{getLabelValue $service "frontend.priority" ""}} - {{end}} - - {{if hasLabel $service "frontend.basicAuth"}} - basicAuth = {{getLabelValue $service "frontend.basicAuth" ""}} - {{end}} - - {{if hasLabel $service "frontend.entryPoints"}} - entryPoints = {{getLabelValue $service "frontend.entryPoints" ""}} - {{end}} - - {{range $key, $value := getLabelsWithPrefix $service "frontend.rule"}} - [frontends."{{$service.Name}}".routes."{{$key}}"] - rule = "{{$value}}" - {{end}} - - {{else if eq $service.ServiceKind "Stateful"}} - {{range $partition := $service.Partitions}} - {{$partitionId := $partition.PartitionInformation.ID}} - - {{if hasLabel $service "frontend.rule"}} - [frontends."{{$service.Name}}/{{$partitionId}}"] - backend = "{{getBackendName $service.Name $partition}}" - [frontends."{{$service.Name}}/{{$partitionId}}".routes.default] - rule = {{getLabelValue $service "frontend.rule.partition.$partitionId" ""}} {{end}} + {{end}} {{end}} {{end}} + +[frontends] +{{range $groupName, $groupServices := getGroupedServices .Services }} + {{ $service := index $groupServices 0 }} + [frontends."{{ $groupName }}"] + backend = "{{ $groupName }}" + priority = 50 + + {{range $key, $value := getFrontendRules $service }} + [frontends."{{ $groupName }}".routes."{{ $key }}"] + rule = "{{ $value }}" + {{end}} +{{end}} + +{{range $service := .Services }} + {{if isEnabled $service }} + {{ $frontendName := $service.Name }} + + {{if isStateless $service }} + + [frontends."frontend-{{ $frontendName }}"] + backend = "{{ $service.Name }}" + passHostHeader = {{ getPassHostHeader $service }} + passTLSCert = {{ getPassTLSCert $service }} + priority = {{ getPriority $service }} + + {{ $entryPoints := getEntryPoints $service }} + {{if $entryPoints }} + entryPoints = [{{range $entryPoints }} + "{{.}}", + {{end}}] + {{end}} + + {{ $whitelistSourceRange := getWhitelistSourceRange $service }} + {{if $whitelistSourceRange }} + whitelistSourceRange = [{{range $whitelistSourceRange }} + "{{.}}", + {{end}}] + {{end}} + + {{ $basicAuth := getBasicAuth $service }} + {{if $basicAuth }} + basicAuth = [{{range $basicAuth }} + "{{.}}", + {{end}}] + {{end}} + + {{ $headers := getHeaders $service }} + {{if $headers }} + [frontends."frontend-{{ $frontendName }}".headers] + SSLRedirect = {{ $headers.SSLRedirect }} + SSLTemporaryRedirect = {{ $headers.SSLTemporaryRedirect }} + SSLHost = "{{ $headers.SSLHost }}" + STSSeconds = {{ $headers.STSSeconds }} + STSIncludeSubdomains = {{ $headers.STSIncludeSubdomains }} + STSPreload = {{ $headers.STSPreload }} + ForceSTSHeader = {{ $headers.ForceSTSHeader }} + FrameDeny = {{ $headers.FrameDeny }} + CustomFrameOptionsValue = "{{ $headers.CustomFrameOptionsValue }}" + ContentTypeNosniff = {{ $headers.ContentTypeNosniff }} + BrowserXSSFilter = {{ $headers.BrowserXSSFilter }} + CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}" + ContentSecurityPolicy = "{{ $headers.ContentSecurityPolicy }}" + PublicKey = "{{ $headers.PublicKey }}" + ReferrerPolicy = "{{ $headers.ReferrerPolicy }}" + IsDevelopment = {{ $headers.IsDevelopment }} + + {{if $headers.AllowedHosts }} + AllowedHosts = [{{range $headers.AllowedHosts }} + "{{.}}", + {{end}}] + {{end}} + + {{if $headers.HostsProxyHeaders }} + HostsProxyHeaders = [{{range $headers.HostsProxyHeaders }} + "{{.}}", + {{end}}] + {{end}} + + {{if $headers.CustomRequestHeaders }} + [frontends."frontend-{{ $frontendName }}".headers.customRequestHeaders] + {{range $k, $v := $headers.CustomRequestHeaders }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + + {{if $headers.CustomResponseHeaders }} + [frontends."frontend-{{ $frontendName }}".headers.customResponseHeaders] + {{range $k, $v := $headers.CustomResponseHeaders }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + + {{if $headers.SSLProxyHeaders }} + [frontends."frontend-{{ $frontendName }}".headers.SSLProxyHeaders] + {{range $k, $v := $headers.SSLProxyHeaders }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + {{end}} + + {{range $key, $value := getFrontendRules $service }} + [frontends."frontend-{{ $frontendName }}".routes."{{ $key }}"] + rule = "{{ $value }}" + {{end}} + + {{else if isStateful $service}} + + {{range $partition := $service.Partitions }} + {{ $partitionId := $partition.PartitionInformation.ID }} + + {{if hasLabel $service "frontend.rule" }} + [frontends."{{ $service.Name }}/{{ $partitionId }}"] + backend = "{{ getBackendName $service.Name $partition }}" + + [frontends."{{ $service.Name }}/{{ $partitionId }}".routes.default] + rule = {{ getLabelValue $service "frontend.rule.partition.$partitionId" "" }} + {{end}} + {{end}} + + {{end}} + + {{end}} {{end}} ` diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/types.go b/vendor/github.com/containous/traefik-extra-service-fabric/types.go index 1681314d7..beb1e8dbf 100644 --- a/vendor/github.com/containous/traefik-extra-service-fabric/types.go +++ b/vendor/github.com/containous/traefik-extra-service-fabric/types.go @@ -9,11 +9,9 @@ import ( // it belongs too and the replicas/partitions type ServiceItemExtended struct { sf.ServiceItem - HasHTTPEndpoint bool - IsHealthy bool - Application sf.ApplicationItem - Partitions []PartitionItemExtended - Labels map[string]string + Application sf.ApplicationItem + Partitions []PartitionItemExtended + Labels map[string]string } // PartitionItemExtended provides a flattened view @@ -32,8 +30,9 @@ type sfClient interface { GetPartitions(appName, serviceName string) (*sf.PartitionItemsPage, error) GetReplicas(appName, serviceName, partitionName string) (*sf.ReplicaItemsPage, error) GetInstances(appName, serviceName, partitionName string) (*sf.InstanceItemsPage, error) - GetServiceExtension(appType, applicationVersion, serviceTypeName, extensionKey string, response interface{}) error + GetServiceExtensionMap(service *sf.ServiceItem, app *sf.ApplicationItem, extensionKey string) (map[string]string, error) GetServiceLabels(service *sf.ServiceItem, app *sf.ApplicationItem, prefix string) (map[string]string, error) + GetProperties(name string) (bool, map[string]string, error) } // replicaInstance interface provides a unified interface diff --git a/vendor/github.com/jjcollinge/servicefabric/servicefabric.go b/vendor/github.com/jjcollinge/servicefabric/servicefabric.go index 7500ee658..838da6c6f 100644 --- a/vendor/github.com/jjcollinge/servicefabric/servicefabric.go +++ b/vendor/github.com/jjcollinge/servicefabric/servicefabric.go @@ -219,8 +219,30 @@ func (c Client) GetServiceExtension(appType, applicationVersion, serviceTypeName return nil } +// GetServiceExtensionMap returns all the extension xml specified +// in a Service's manifest file into (which must conform to ServiceExtensionLabels) +// a map[string]string +func (c Client) GetServiceExtensionMap(service *ServiceItem, app *ApplicationItem, extensionKey string) (map[string]string, error) { + extensionData := ServiceExtensionLabels{} + err := c.GetServiceExtension(app.TypeName, app.TypeVersion, service.TypeName, extensionKey, &extensionData) + if err != nil { + return nil, err + } + + labels := map[string]string{} + if extensionData.Label != nil { + for _, label := range extensionData.Label { + labels[label.Key] = label.Value + } + } + + return labels, nil +} + // GetProperties uses the Property Manager API to retrieve // string properties from a name as a dictionary +// Property name is the path to the properties you would like to list. +// for example a serviceID func (c Client) GetProperties(name string) (bool, map[string]string, error) { nameExists, err := c.nameExists(name) if err != nil { @@ -264,6 +286,8 @@ func (c Client) GetProperties(name string) (bool, map[string]string, error) { // GetServiceLabels add labels from service manifest extensions and properties manager // expects extension xml in +// +// Deprecated: Use GetProperties and GetServiceExtensionMap instead. func (c Client) GetServiceLabels(service *ServiceItem, app *ApplicationItem, prefix string) (map[string]string, error) { extensionData := ServiceExtensionLabels{} err := c.GetServiceExtension(app.TypeName, app.TypeVersion, service.TypeName, prefix, &extensionData)