Add Service Fabric Provider
This commit is contained in:
parent
9f6f637527
commit
39c1cc1b3c
21 changed files with 1624 additions and 18 deletions
|
@ -4,6 +4,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/flaeg"
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/traefik-extra-service-fabric"
|
||||||
"github.com/containous/traefik/api"
|
"github.com/containous/traefik/api"
|
||||||
"github.com/containous/traefik/configuration"
|
"github.com/containous/traefik/configuration"
|
||||||
"github.com/containous/traefik/middlewares/accesslog"
|
"github.com/containous/traefik/middlewares/accesslog"
|
||||||
|
@ -23,6 +24,7 @@ import (
|
||||||
"github.com/containous/traefik/provider/rest"
|
"github.com/containous/traefik/provider/rest"
|
||||||
"github.com/containous/traefik/provider/zk"
|
"github.com/containous/traefik/provider/zk"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
|
sf "github.com/jjcollinge/servicefabric"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TraefikConfiguration holds GlobalConfiguration and other stuff
|
// TraefikConfiguration holds GlobalConfiguration and other stuff
|
||||||
|
@ -163,6 +165,11 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||||
var defaultEureka eureka.Provider
|
var defaultEureka eureka.Provider
|
||||||
defaultEureka.Delay = "30s"
|
defaultEureka.Delay = "30s"
|
||||||
|
|
||||||
|
// default ServiceFabric
|
||||||
|
var defaultServiceFabric servicefabric.Provider
|
||||||
|
defaultServiceFabric.APIVersion = sf.DefaultAPIVersion
|
||||||
|
defaultServiceFabric.RefreshSeconds = 10
|
||||||
|
|
||||||
// default Ping
|
// default Ping
|
||||||
var defaultPing = ping.Handler{
|
var defaultPing = ping.Handler{
|
||||||
EntryPoint: "traefik",
|
EntryPoint: "traefik",
|
||||||
|
@ -196,7 +203,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
// default LifeCycle
|
// default LifeCycle
|
||||||
defaultLifeycle := configuration.LifeCycle{
|
defaultLifeCycle := configuration.LifeCycle{
|
||||||
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +259,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||||
ForwardingTimeouts: &forwardingTimeouts,
|
ForwardingTimeouts: &forwardingTimeouts,
|
||||||
TraefikLog: &defaultTraefikLog,
|
TraefikLog: &defaultTraefikLog,
|
||||||
AccessLog: &defaultAccessLog,
|
AccessLog: &defaultAccessLog,
|
||||||
LifeCycle: &defaultLifeycle,
|
LifeCycle: &defaultLifeCycle,
|
||||||
Ping: &defaultPing,
|
Ping: &defaultPing,
|
||||||
API: &defaultAPI,
|
API: &defaultAPI,
|
||||||
Metrics: &defaultMetrics,
|
Metrics: &defaultMetrics,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/flaeg"
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/traefik-extra-service-fabric"
|
||||||
"github.com/containous/traefik/acme"
|
"github.com/containous/traefik/acme"
|
||||||
"github.com/containous/traefik/api"
|
"github.com/containous/traefik/api"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
|
@ -88,6 +89,7 @@ type GlobalConfiguration struct {
|
||||||
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
||||||
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
|
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
|
||||||
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
|
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
|
||||||
|
ServiceFabric *servicefabric.Provider `description:"Enable Service Fabric backend with default settings" export:"true"`
|
||||||
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
|
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
|
||||||
API *api.Handler `description:"Enable api/dashboard" export:"true"`
|
API *api.Handler `description:"Enable api/dashboard" export:"true"`
|
||||||
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
||||||
|
|
107
docs/configuration/backends/servicefabric.md
Normal file
107
docs/configuration/backends/servicefabric.md
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
# Service Fabric Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Service Fabric as a backend configuration.
|
||||||
|
|
||||||
|
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
|
||||||
|
|
||||||
|
## Service Fabric
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Service Fabric provider
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Service Fabric configuration backend
|
||||||
|
[serviceFabric]
|
||||||
|
|
||||||
|
# Service Fabric Management Endpoint
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
clusterManagementUrl = "https://localhost:19080"
|
||||||
|
|
||||||
|
# Service Fabric Management Endpoint API Version
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "3.0"
|
||||||
|
#
|
||||||
|
apiVersion = "3.0"
|
||||||
|
|
||||||
|
# Enable TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [serviceFabric.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/servicefabric.crt"
|
||||||
|
# key = "/etc/ssl/servicefabric.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
|
||||||
|
The provider uses labels to configure how services are exposed through Træfik.
|
||||||
|
These can be set using Extensions and the Property Manager API
|
||||||
|
|
||||||
|
#### Extensions
|
||||||
|
|
||||||
|
Set labels with extensions through the services `ServiceManifest.xml` file.
|
||||||
|
Here is an example of an extension setting Træfik labels:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<StatelessServiceType ServiceTypeName="WebServiceType">
|
||||||
|
<Extensions>
|
||||||
|
<Extension Name="Traefik">
|
||||||
|
<Labels xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
|
||||||
|
<Label Key="traefik.frontend.rule.example2">PathPrefixStrip: /a/path/to/strip</Label>
|
||||||
|
<Label Key="traefik.expose">true</Label>
|
||||||
|
<Label Key="traefik.frontend.passHostHeader">true</Label>
|
||||||
|
</Labels>
|
||||||
|
</Extension>
|
||||||
|
</Extensions>
|
||||||
|
</StatelessServiceType>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -X PUT \
|
||||||
|
'http://localhost:19080/Names/GettingStartedApplication2/WebService/$/GetProperty?api-version=6.0&IncludeValues=true' \
|
||||||
|
-d '{
|
||||||
|
"PropertyName": "traefik.frontend.rule.default",
|
||||||
|
"Value": {
|
||||||
|
"Kind": "String",
|
||||||
|
"Data": "PathPrefixStrip: /a/path/to/strip"
|
||||||
|
},
|
||||||
|
"CustomTypeId": "LabelType"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
This functionality will be released in a future version of the [sfctl](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-lifecycle-sfctl) tool.
|
||||||
|
|
||||||
|
## Available Labels
|
||||||
|
|
||||||
|
Labels, set through extensions or the property manager, can be used on services to override default behaviour.
|
||||||
|
|
||||||
|
| Label | Description |
|
||||||
|
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>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.<br>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.<br>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 |
|
12
glide.lock
generated
12
glide.lock
generated
|
@ -1,5 +1,5 @@
|
||||||
hash: 4ac4017b19d4a7355894a09cd6d1f2b92729f252578b6a044a3ff2dea22133c4
|
hash: e17ccb5f089116a333c1d2ccba9f5d710bd28779b7d4461e37a1c301c2a00fba
|
||||||
updated: 2017-11-21T21:24:24.601164724+01:00
|
updated: 2017-11-22T15:59:09.002650953+01:00
|
||||||
imports:
|
imports:
|
||||||
- name: cloud.google.com/go
|
- name: cloud.google.com/go
|
||||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||||
|
@ -93,6 +93,8 @@ imports:
|
||||||
version: 06ccd3e75091eb659b1d720cda0e16bc7057954c
|
version: 06ccd3e75091eb659b1d720cda0e16bc7057954c
|
||||||
- name: github.com/containous/staert
|
- name: github.com/containous/staert
|
||||||
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
||||||
|
- name: github.com/containous/traefik-extra-service-fabric
|
||||||
|
version: 43b41061088161b93413eb35bd1496f11347c43b
|
||||||
- name: github.com/coreos/bbolt
|
- name: github.com/coreos/bbolt
|
||||||
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
||||||
- name: github.com/coreos/etcd
|
- name: github.com/coreos/etcd
|
||||||
|
@ -334,7 +336,7 @@ imports:
|
||||||
- name: github.com/huandu/xstrings
|
- name: github.com/huandu/xstrings
|
||||||
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
||||||
- name: github.com/imdario/mergo
|
- name: github.com/imdario/mergo
|
||||||
version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc
|
version: 7fe0c75c13abdee74b09fcacef5ea1c6bba6a874
|
||||||
- name: github.com/influxdata/influxdb
|
- name: github.com/influxdata/influxdb
|
||||||
version: 2d474a3089bcfce6b472779be9470a1f0ef3d5e4
|
version: 2d474a3089bcfce6b472779be9470a1f0ef3d5e4
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -345,6 +347,8 @@ imports:
|
||||||
version: 2fd0705ce648e602e6c9c57329a174270a4f6688
|
version: 2fd0705ce648e602e6c9c57329a174270a4f6688
|
||||||
subpackages:
|
subpackages:
|
||||||
- lib
|
- lib
|
||||||
|
- name: github.com/jjcollinge/servicefabric
|
||||||
|
version: 93a44e59fc887cda489913c6fc5bda834989f3bd
|
||||||
- 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
|
||||||
|
@ -368,7 +372,7 @@ imports:
|
||||||
- name: github.com/Masterminds/semver
|
- name: github.com/Masterminds/semver
|
||||||
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||||
- name: github.com/Masterminds/sprig
|
- name: github.com/Masterminds/sprig
|
||||||
version: 9526be0327b26ad31aa70296a7b10704883976d5
|
version: e039e20e500c2c025d9145be375e27cf42a94174
|
||||||
- name: github.com/mattn/go-colorable
|
- name: github.com/mattn/go-colorable
|
||||||
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
||||||
repo: https://github.com/mattn/go-colorable
|
repo: https://github.com/mattn/go-colorable
|
||||||
|
|
|
@ -11,6 +11,8 @@ import:
|
||||||
version: 10f801ebc38b33738c9d17d50860f484a0988ff5
|
version: 10f801ebc38b33738c9d17d50860f484a0988ff5
|
||||||
- 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
|
||||||
|
version: ^v1.0.0
|
||||||
- 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
|
||||||
|
|
|
@ -85,6 +85,7 @@ pages:
|
||||||
- 'Backend: Mesos': 'configuration/backends/mesos.md'
|
- 'Backend: Mesos': 'configuration/backends/mesos.md'
|
||||||
- 'Backend: Rancher': 'configuration/backends/rancher.md'
|
- 'Backend: Rancher': 'configuration/backends/rancher.md'
|
||||||
- 'Backend: Rest': 'configuration/backends/rest.md'
|
- 'Backend: Rest': 'configuration/backends/rest.md'
|
||||||
|
- 'Backend: Service Fabric': 'configuration/backends/servicefabric.md'
|
||||||
- 'Backend: Zookeeper': 'configuration/backends/zookeeper.md'
|
- 'Backend: Zookeeper': 'configuration/backends/zookeeper.md'
|
||||||
- 'API / Dashboard': 'configuration/api.md'
|
- 'API / Dashboard': 'configuration/api.md'
|
||||||
- 'Ping': 'configuration/ping.md'
|
- 'Ping': 'configuration/ping.md'
|
||||||
|
|
|
@ -559,6 +559,9 @@ func (s *Server) configureProviders() {
|
||||||
if s.globalConfiguration.DynamoDB != nil {
|
if s.globalConfiguration.DynamoDB != nil {
|
||||||
s.providers = append(s.providers, s.globalConfiguration.DynamoDB)
|
s.providers = append(s.providers, s.globalConfiguration.DynamoDB)
|
||||||
}
|
}
|
||||||
|
if s.globalConfiguration.ServiceFabric != nil {
|
||||||
|
s.providers = append(s.providers, s.globalConfiguration.ServiceFabric)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) startProviders() {
|
func (s *Server) startProviders() {
|
||||||
|
|
11
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
11
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
|
@ -183,6 +183,9 @@ var genericMap = map[string]interface{}{
|
||||||
"biggest": max,
|
"biggest": max,
|
||||||
"max": max,
|
"max": max,
|
||||||
"min": min,
|
"min": min,
|
||||||
|
"ceil": ceil,
|
||||||
|
"floor": floor,
|
||||||
|
"round": round,
|
||||||
|
|
||||||
// string slices. Note that we reverse the order b/c that's better
|
// string slices. Note that we reverse the order b/c that's better
|
||||||
// for template processing.
|
// for template processing.
|
||||||
|
@ -258,4 +261,12 @@ var genericMap = map[string]interface{}{
|
||||||
|
|
||||||
// Flow Control:
|
// Flow Control:
|
||||||
"fail": func(msg string) (string, error) { return "", errors.New(msg) },
|
"fail": func(msg string) (string, error) { return "", errors.New(msg) },
|
||||||
|
|
||||||
|
// Regex
|
||||||
|
"regexMatch": regexMatch,
|
||||||
|
"regexFindAll": regexFindAll,
|
||||||
|
"regexFind": regexFind,
|
||||||
|
"regexReplaceAll": regexReplaceAll,
|
||||||
|
"regexReplaceAllLiteral": regexReplaceAllLiteral,
|
||||||
|
"regexSplit": regexSplit,
|
||||||
}
|
}
|
||||||
|
|
30
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
30
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
|
@ -127,3 +127,33 @@ func untilStep(start, stop, step int) []int {
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func floor(a interface{}) float64 {
|
||||||
|
aa := toFloat64(a)
|
||||||
|
return math.Floor(aa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ceil(a interface{}) float64 {
|
||||||
|
aa := toFloat64(a)
|
||||||
|
return math.Ceil(aa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func round(a interface{}, p int, r_opt ...float64) float64 {
|
||||||
|
roundOn := .5
|
||||||
|
if len(r_opt) > 0 {
|
||||||
|
roundOn = r_opt[0]
|
||||||
|
}
|
||||||
|
val := toFloat64(a)
|
||||||
|
places := toFloat64(p)
|
||||||
|
|
||||||
|
var round float64
|
||||||
|
pow := math.Pow(10, places)
|
||||||
|
digit := pow * val
|
||||||
|
_, div := math.Modf(digit)
|
||||||
|
if div >= roundOn {
|
||||||
|
round = math.Ceil(digit)
|
||||||
|
} else {
|
||||||
|
round = math.Floor(digit)
|
||||||
|
}
|
||||||
|
return round / pow
|
||||||
|
}
|
35
vendor/github.com/Masterminds/sprig/regex.go
generated
vendored
Normal file
35
vendor/github.com/Masterminds/sprig/regex.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func regexMatch(regex string, s string) bool {
|
||||||
|
match, _ := regexp.MatchString(regex, s)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexFindAll(regex string, s string, n int) []string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.FindAllString(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexFind(regex string, s string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.FindString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexReplaceAll(regex string, s string, repl string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.ReplaceAllString(s, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexReplaceAllLiteral(regex string, s string, repl string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.ReplaceAllLiteralString(s, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexSplit(regex string, s string, n int) []string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.Split(s, n)
|
||||||
|
}
|
201
vendor/github.com/containous/traefik-extra-service-fabric/LICENSE
generated
vendored
Normal file
201
vendor/github.com/containous/traefik-extra-service-fabric/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2017 Containous
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
358
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
Normal file
358
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
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/safe"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
sf "github.com/jjcollinge/servicefabric"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
|
const traefikLabelPrefix = "traefik"
|
||||||
|
|
||||||
|
// Provider holds for configuration for the provider
|
||||||
|
type Provider struct {
|
||||||
|
provider.BaseProvider `mapstructure:",squash"`
|
||||||
|
ClusterManagementURL string `description:"Service Fabric API endpoint"`
|
||||||
|
APIVersion string `description:"Service Fabric API version" export:"true"`
|
||||||
|
RefreshSeconds int `description:"Polling interval (in seconds)" export:"true"`
|
||||||
|
TLS *types.ClientTLS `description:"Enable TLS support" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide allows the ServiceFabric provider to provide configurations to traefik
|
||||||
|
// using the given configuration channel.
|
||||||
|
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
||||||
|
if p.APIVersion == "" {
|
||||||
|
p.APIVersion = sf.DefaultAPIVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig, err := p.TLS.CreateTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sfClient, err := sf.NewClient(http.DefaultClient, p.ClusterManagementURL, p.APIVersion, tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.RefreshSeconds <= 0 {
|
||||||
|
p.RefreshSeconds = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.updateConfig(configurationChan, pool, sfClient, time.Duration(p.RefreshSeconds)*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, sfClient sfClient, pollInterval time.Duration) error {
|
||||||
|
pool.Go(func(stop chan bool) {
|
||||||
|
operation := func() error {
|
||||||
|
ticker := time.NewTicker(pollInterval)
|
||||||
|
for range ticker.C {
|
||||||
|
select {
|
||||||
|
case shouldStop := <-stop:
|
||||||
|
if shouldStop {
|
||||||
|
ticker.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Info("Checking service fabric config")
|
||||||
|
}
|
||||||
|
|
||||||
|
services, err := getClusterServices(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,
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration, err := p.GetConfiguration(tmpl, sfFuncMap, templateObjects)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configurationChan <- types.ConfigMessage{
|
||||||
|
ProviderName: "servicefabric",
|
||||||
|
Configuration: configuration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
notify := func(err error, time time.Duration) {
|
||||||
|
log.Errorf("Provider connection error: %v; retrying in %s", err, time)
|
||||||
|
}
|
||||||
|
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Cannot connect to Provider: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Provider) doesAppParamContain(app sf.ApplicationItem, key, shouldContain string) bool {
|
||||||
|
value := p.getApplicationParameter(app, key)
|
||||||
|
return strings.Contains(value, shouldContain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Provider) 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 (p Provider) getDefaultEndpoint(instance replicaInstance) string {
|
||||||
|
id, data := instance.GetReplicaData()
|
||||||
|
endpoint, err := getDefaultEndpoint(data.Address)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Provider) getNamedEndpoint(instance replicaInstance, endpointName string) string {
|
||||||
|
id, data := instance.GetReplicaData()
|
||||||
|
endpoint, err := getNamedEndpoint(data.Address, endpointName)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("No names endpoint of %s for replica %s in endpointData: %s", endpointName, id, data.Address)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) {
|
||||||
|
apps, err := sfClient.GetApplications()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []ServiceItemExtended
|
||||||
|
for _, app := range apps.Items {
|
||||||
|
services, err := sfClient.GetServices(app.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range services.Items {
|
||||||
|
item := ServiceItemExtended{
|
||||||
|
ServiceItem: service,
|
||||||
|
Application: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
if labels, err := sfClient.GetServiceLabels(&service, &app, traefikLabelPrefix); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
} else {
|
||||||
|
item.Labels = labels
|
||||||
|
}
|
||||||
|
|
||||||
|
if partitions, err := sfClient.GetPartitions(app.ID, service.ID); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
} else {
|
||||||
|
for _, partition := range partitions.Items {
|
||||||
|
partitionExt := PartitionItemExtended{PartitionItem: partition}
|
||||||
|
|
||||||
|
if partition.ServiceKind == "Stateful" {
|
||||||
|
partitionExt.Replicas = getValidReplicas(sfClient, app, service, partition)
|
||||||
|
} else if partition.ServiceKind == "Stateless" {
|
||||||
|
partitionExt.Instances = getValidInstances(sfClient, app, service, partition)
|
||||||
|
} else {
|
||||||
|
log.Errorf("Unsupported service kind %s in service %s", partition.ServiceKind, service.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Partitions = append(item.Partitions, partitionExt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results = append(results, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValidReplicas(sfClient sfClient, app sf.ApplicationItem, service sf.ServiceItem, partition sf.PartitionItem) []sf.ReplicaItem {
|
||||||
|
var validReplicas []sf.ReplicaItem
|
||||||
|
|
||||||
|
if replicas, err := sfClient.GetReplicas(app.ID, service.ID, partition.PartitionInformation.ID); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
} else {
|
||||||
|
for _, instance := range replicas.Items {
|
||||||
|
if isHealthy(instance.ReplicaItemBase) && hasHTTPEndpoint(instance.ReplicaItemBase) {
|
||||||
|
validReplicas = append(validReplicas, instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validReplicas
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValidInstances(sfClient sfClient, app sf.ApplicationItem, service sf.ServiceItem, partition sf.PartitionItem) []sf.InstanceItem {
|
||||||
|
var validInstances []sf.InstanceItem
|
||||||
|
|
||||||
|
if instances, err := sfClient.GetInstances(app.ID, service.ID, partition.PartitionInformation.ID); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
} else {
|
||||||
|
for _, instance := range instances.Items {
|
||||||
|
if isHealthy(instance.ReplicaItemBase) && hasHTTPEndpoint(instance.ReplicaItemBase) {
|
||||||
|
validInstances = append(validInstances, instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validInstances
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasServiceLabel(service ServiceItemExtended, key string) bool {
|
||||||
|
_, exists := service.Labels[key]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceLabelValue(service ServiceItemExtended, key string) string {
|
||||||
|
return service.Labels[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServicesWithLabelValueMap(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 getServicesWithLabelValue(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 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 {
|
||||||
|
_, data := instance.GetReplicaData()
|
||||||
|
return data.ReplicaRole == "Primary"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHealthy(instanceData *sf.ReplicaItemBase) bool {
|
||||||
|
return instanceData != nil && (instanceData.ReplicaStatus == "Ready" || instanceData.HealthState != "Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool {
|
||||||
|
_, err := getDefaultEndpoint(instanceData.Address)
|
||||||
|
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)
|
||||||
|
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 getDefaultEndpoint(endpointData string) (string, error) {
|
||||||
|
endpoints, err := decodeEndpointData(endpointData)
|
||||||
|
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
|
||||||
|
}
|
141
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go
generated
vendored
Normal file
141
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package servicefabric
|
||||||
|
|
||||||
|
const tmpl = `
|
||||||
|
[backends]
|
||||||
|
{{$groupedServiceMap := getServicesWithLabelValueMap .Services "backend.group.name"}}
|
||||||
|
{{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 = {{getServiceLabelValueWithDefault $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 hasServiceLabel $service "backend.loadbalancer.method"}}
|
||||||
|
method = "{{getServiceLabelValue $service "backend.loadbalancer.method" }}"
|
||||||
|
{{else}}
|
||||||
|
method = "drr"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "backend.healthcheck"}}
|
||||||
|
[backends."{{$service.Name}}".healthcheck]
|
||||||
|
path = "{{getServiceLabelValue $service "backend.healthcheck"}}"
|
||||||
|
interval = "{{getServiceLabelValueWithDefault $service "backend.healthcheck.interval" "10s"}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "backend.loadbalancer.stickiness"}}
|
||||||
|
[backends."{{$service.Name}}".LoadBalancer.stickiness]
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "backend.circuitbreaker"}}
|
||||||
|
[backends."{{$service.Name}}".circuitbreaker]
|
||||||
|
expression = "{{getServiceLabelValue $service "backend.circuitbreaker"}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "backend.maxconn.amount"}}
|
||||||
|
[backends."{{$service.Name}}".maxconn]
|
||||||
|
amount = {{getServiceLabelValue $service "backend.maxconn.amount"}}
|
||||||
|
{{if hasServiceLabel $service "backend.maxconn.extractorfunc"}}
|
||||||
|
extractorfunc = "{{getServiceLabelValue $service "backend.maxconn.extractorfunc"}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $instance := $partition.Instances}}
|
||||||
|
[backends."{{$service.Name}}".servers."{{$instance.ID}}"]
|
||||||
|
url = "{{getDefaultEndpoint $instance}}"
|
||||||
|
weight = {{getServiceLabelValueWithDefault $service "backend.weight" "1"}}
|
||||||
|
{{end}}
|
||||||
|
{{else if eq $partition.ServiceKind "Stateful"}}
|
||||||
|
{{range $replica := $partition.Replicas}}
|
||||||
|
{{if isPrimary $replica}}
|
||||||
|
|
||||||
|
{{$backendName := (print $service.Name $partition.PartitionInformation.ID)}}
|
||||||
|
[backends."{{$backendName}}".servers."{{$replica.ID}}"]
|
||||||
|
url = "{{getDefaultEndpoint $replica}}"
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[backends."{{$backendName}}".LoadBalancer]
|
||||||
|
method = "drr"
|
||||||
|
|
||||||
|
[backends."{{$backendName}}".circuitbreaker]
|
||||||
|
expression = "NetworkErrorRatio() > 0.5"
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
{{range $groupName, $groupServices := $groupedServiceMap}}
|
||||||
|
{{$service := index $groupServices 0}}
|
||||||
|
[frontends."{{$groupName}}"]
|
||||||
|
backend = "{{$groupName}}"
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "frontend.priority"}}
|
||||||
|
priority = 100
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $key, $value := getServiceLabelsWithPrefix $service "frontend.rule"}}
|
||||||
|
[frontends."{{$groupName}}".routes."{{$key}}"]
|
||||||
|
rule = "{{$value}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{range $service := .Services}}
|
||||||
|
{{if hasServiceLabel $service "expose"}}
|
||||||
|
{{if eq $service.ServiceKind "Stateless"}}
|
||||||
|
|
||||||
|
[frontends."{{$service.Name}}"]
|
||||||
|
backend = "{{$service.Name}}"
|
||||||
|
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "frontend.passHostHeader"}}
|
||||||
|
passHostHeader = {{getServiceLabelValue $service "frontend.passHostHeader" }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "frontend.whitelistSourceRange"}}
|
||||||
|
whitelistSourceRange = {{getServiceLabelValue $service "frontend.whitelistSourceRange" }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "frontend.priority"}}
|
||||||
|
priority = {{getServiceLabelValue $service "frontend.priority"}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "frontend.basicAuth"}}
|
||||||
|
basicAuth = {{getServiceLabelValue $service "frontend.basicAuth"}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasServiceLabel $service "frontend.entryPoints"}}
|
||||||
|
entryPoints = {{getServiceLabelValue $service "frontend.entryPoints"}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $key, $value := getServiceLabelsWithPrefix $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 hasServiceLabel $service "frontend.rule"}}
|
||||||
|
[frontends."{{$service.Name}}/{{$partitionId}}"]
|
||||||
|
backend = "{{$service.Name}}/{{$partitionId}}"
|
||||||
|
[frontends."{{$service.Name}}/{{$partitionId}}".routes.default]
|
||||||
|
rule = {{getServiceLabelValue $service "frontend.rule.partition.$partitionId"}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
`
|
43
vendor/github.com/containous/traefik-extra-service-fabric/types.go
generated
vendored
Normal file
43
vendor/github.com/containous/traefik-extra-service-fabric/types.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package servicefabric
|
||||||
|
|
||||||
|
import (
|
||||||
|
sf "github.com/jjcollinge/servicefabric"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceItemExtended provides a flattened view
|
||||||
|
// of the service with details of the application
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartitionItemExtended provides a flattened view
|
||||||
|
// of a services partitions
|
||||||
|
type PartitionItemExtended struct {
|
||||||
|
sf.PartitionItem
|
||||||
|
Replicas []sf.ReplicaItem
|
||||||
|
Instances []sf.InstanceItem
|
||||||
|
}
|
||||||
|
|
||||||
|
// sfClient is an interface for Service Fabric client's to implement.
|
||||||
|
// This is purposely a subset of the total Service Fabric API surface.
|
||||||
|
type sfClient interface {
|
||||||
|
GetApplications() (*sf.ApplicationItemsPage, error)
|
||||||
|
GetServices(appName string) (*sf.ServiceItemsPage, error)
|
||||||
|
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
|
||||||
|
GetServiceLabels(service *sf.ServiceItem, app *sf.ApplicationItem, prefix string) (map[string]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replicaInstance interface provides a unified interface
|
||||||
|
// over replicas and instances
|
||||||
|
type replicaInstance interface {
|
||||||
|
GetReplicaData() (string, *sf.ReplicaItemBase)
|
||||||
|
}
|
24
vendor/github.com/imdario/mergo/map.go
generated
vendored
24
vendor/github.com/imdario/mergo/map.go
generated
vendored
|
@ -61,6 +61,13 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
|
||||||
dstMap[fieldName] = src.Field(i).Interface()
|
dstMap[fieldName] = src.Field(i).Interface()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
if dst.IsNil() {
|
||||||
|
v := reflect.New(dst.Type().Elem())
|
||||||
|
dst.Set(v)
|
||||||
|
}
|
||||||
|
dst = dst.Elem()
|
||||||
|
fallthrough
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
srcMap := src.Interface().(map[string]interface{})
|
srcMap := src.Interface().(map[string]interface{})
|
||||||
for key := range srcMap {
|
for key := range srcMap {
|
||||||
|
@ -85,6 +92,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
|
||||||
srcKind = reflect.Ptr
|
srcKind = reflect.Ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !srcElement.IsValid() {
|
if !srcElement.IsValid() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -92,14 +100,16 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
|
||||||
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
|
||||||
if srcKind == reflect.Map {
|
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||||
if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
|
|
||||||
}
|
}
|
||||||
|
} else if srcKind == reflect.Map {
|
||||||
|
if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
vendor/github.com/imdario/mergo/merge.go
generated
vendored
52
vendor/github.com/imdario/mergo/merge.go
generated
vendored
|
@ -12,6 +12,18 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func hasExportedField(dst reflect.Value) (exported bool) {
|
||||||
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||||
|
field := dst.Type().Field(i)
|
||||||
|
if field.Anonymous {
|
||||||
|
exported = exported || hasExportedField(dst.Field(i))
|
||||||
|
} else {
|
||||||
|
exported = exported || len(field.PkgPath) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Traverses recursively both values, assigning src's fields values to dst.
|
// Traverses recursively both values, assigning src's fields values to dst.
|
||||||
// The map argument tracks comparisons that have already been seen, which allows
|
// The map argument tracks comparisons that have already been seen, which allows
|
||||||
// short circuiting on recursive types.
|
// short circuiting on recursive types.
|
||||||
|
@ -34,12 +46,22 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
|
||||||
}
|
}
|
||||||
switch dst.Kind() {
|
switch dst.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
for i, n := 0, dst.NumField(); i < n; i++ {
|
if hasExportedField(dst) {
|
||||||
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||||
return
|
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
|
||||||
|
dst.Set(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
|
if len(src.MapKeys()) == 0 && !src.IsNil() && len(dst.MapKeys()) == 0 {
|
||||||
|
dst.Set(reflect.MakeMap(dst.Type()))
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, key := range src.MapKeys() {
|
for _, key := range src.MapKeys() {
|
||||||
srcElement := src.MapIndex(key)
|
srcElement := src.MapIndex(key)
|
||||||
if !srcElement.IsValid() {
|
if !srcElement.IsValid() {
|
||||||
|
@ -67,6 +89,10 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
|
if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
|
||||||
if dst.IsNil() {
|
if dst.IsNil() {
|
||||||
dst.Set(reflect.MakeMap(dst.Type()))
|
dst.Set(reflect.MakeMap(dst.Type()))
|
||||||
|
@ -77,9 +103,27 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
|
if src.Kind() != reflect.Interface {
|
||||||
|
if dst.IsNil() || overwrite {
|
||||||
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
} else if src.Kind() == reflect.Ptr {
|
||||||
|
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if dst.Elem().Type() == src.Type() {
|
||||||
|
if err = deepMerge(dst.Elem(), src, visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ErrDifferentArgumentsTypes
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
if src.IsNil() {
|
if src.IsNil() {
|
||||||
break
|
break
|
||||||
} else if dst.IsNil() {
|
} else if dst.IsNil() || overwrite {
|
||||||
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
}
|
}
|
||||||
|
|
2
vendor/github.com/imdario/mergo/mergo.go
generated
vendored
2
vendor/github.com/imdario/mergo/mergo.go
generated
vendored
|
@ -45,7 +45,7 @@ func isEmptyValue(v reflect.Value) bool {
|
||||||
return v.Uint() == 0
|
return v.Uint() == 0
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
return v.Float() == 0
|
return v.Float() == 0
|
||||||
case reflect.Interface, reflect.Ptr:
|
case reflect.Interface, reflect.Ptr, reflect.Func:
|
||||||
return v.IsNil()
|
return v.IsNil()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
21
vendor/github.com/jjcollinge/servicefabric/LICENSE
generated
vendored
Normal file
21
vendor/github.com/jjcollinge/servicefabric/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Joni Collinge
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
20
vendor/github.com/jjcollinge/servicefabric/query.go
generated
vendored
Normal file
20
vendor/github.com/jjcollinge/servicefabric/query.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package servicefabric
|
||||||
|
|
||||||
|
type queryParamsFunc func(params []string) []string
|
||||||
|
|
||||||
|
func withContinue(token string) queryParamsFunc {
|
||||||
|
if len(token) == 0 {
|
||||||
|
return noOp
|
||||||
|
}
|
||||||
|
return withParam("continue", token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withParam(name, value string) queryParamsFunc {
|
||||||
|
return func(params []string) []string {
|
||||||
|
return append(params, name+"="+value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func noOp(params []string) []string {
|
||||||
|
return params
|
||||||
|
}
|
367
vendor/github.com/jjcollinge/servicefabric/servicefabric.go
generated
vendored
Normal file
367
vendor/github.com/jjcollinge/servicefabric/servicefabric.go
generated
vendored
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
// Package servicefabric is an opinionated Service Fabric client written in Golang
|
||||||
|
package servicefabric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultAPIVersion = "3.0"
|
||||||
|
|
||||||
|
// Client for Service Fabric.
|
||||||
|
// This is purposely a subset of the total Service Fabric API surface.
|
||||||
|
type Client struct {
|
||||||
|
// endpoint Service Fabric cluster management endpoint
|
||||||
|
endpoint string
|
||||||
|
// apiVersion Service Fabric API version
|
||||||
|
apiVersion string
|
||||||
|
// httpClient HTTP client
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new provider client that can query the
|
||||||
|
// Service Fabric management API externally or internally
|
||||||
|
func NewClient(httpClient *http.Client, endpoint, apiVersion string, tlsConfig *tls.Config) (*Client, error) {
|
||||||
|
if endpoint == "" {
|
||||||
|
return nil, errors.New("endpoint missing for httpClient configuration")
|
||||||
|
}
|
||||||
|
if apiVersion == "" {
|
||||||
|
apiVersion = DefaultAPIVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if tlsConfig != nil {
|
||||||
|
tlsConfig.Renegotiation = tls.RenegotiateFreelyAsClient
|
||||||
|
tlsConfig.BuildNameToCertificate()
|
||||||
|
httpClient.Transport = &http.Transport{TLSClientConfig: tlsConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
endpoint: endpoint,
|
||||||
|
apiVersion: apiVersion,
|
||||||
|
httpClient: httpClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetApplications returns all the registered applications
|
||||||
|
// within the Service Fabric cluster.
|
||||||
|
func (c Client) GetApplications() (*ApplicationItemsPage, error) {
|
||||||
|
var aggregateAppItemsPages ApplicationItemsPage
|
||||||
|
var continueToken string
|
||||||
|
for {
|
||||||
|
res, err := c.getHTTP("Applications/", withContinue(continueToken))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var appItemsPage ApplicationItemsPage
|
||||||
|
err = json.Unmarshal(res, &appItemsPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateAppItemsPages.Items = append(aggregateAppItemsPages.Items, appItemsPage.Items...)
|
||||||
|
|
||||||
|
continueToken = getString(appItemsPage.ContinuationToken)
|
||||||
|
if continueToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &aggregateAppItemsPages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServices returns all the services associated
|
||||||
|
// with a Service Fabric application.
|
||||||
|
func (c Client) GetServices(appName string) (*ServiceItemsPage, error) {
|
||||||
|
var aggregateServiceItemsPages ServiceItemsPage
|
||||||
|
var continueToken string
|
||||||
|
for {
|
||||||
|
res, err := c.getHTTP("Applications/"+appName+"/$/GetServices", withContinue(continueToken))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var servicesItemsPage ServiceItemsPage
|
||||||
|
err = json.Unmarshal(res, &servicesItemsPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateServiceItemsPages.Items = append(aggregateServiceItemsPages.Items, servicesItemsPage.Items...)
|
||||||
|
|
||||||
|
continueToken = getString(servicesItemsPage.ContinuationToken)
|
||||||
|
if continueToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &aggregateServiceItemsPages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPartitions returns all the partitions associated
|
||||||
|
// with a Service Fabric service.
|
||||||
|
func (c Client) GetPartitions(appName, serviceName string) (*PartitionItemsPage, error) {
|
||||||
|
var aggregatePartitionItemsPages PartitionItemsPage
|
||||||
|
var continueToken string
|
||||||
|
for {
|
||||||
|
basePath := "Applications/" + appName + "/$/GetServices/" + serviceName + "/$/GetPartitions/"
|
||||||
|
res, err := c.getHTTP(basePath, withContinue(continueToken))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var partitionsItemsPage PartitionItemsPage
|
||||||
|
err = json.Unmarshal(res, &partitionsItemsPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregatePartitionItemsPages.Items = append(aggregatePartitionItemsPages.Items, partitionsItemsPage.Items...)
|
||||||
|
|
||||||
|
continueToken = getString(partitionsItemsPage.ContinuationToken)
|
||||||
|
if continueToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &aggregatePartitionItemsPages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstances returns all the instances associated
|
||||||
|
// with a stateless Service Fabric partition.
|
||||||
|
func (c Client) GetInstances(appName, serviceName, partitionName string) (*InstanceItemsPage, error) {
|
||||||
|
var aggregateInstanceItemsPages InstanceItemsPage
|
||||||
|
var continueToken string
|
||||||
|
for {
|
||||||
|
basePath := "Applications/" + appName + "/$/GetServices/" + serviceName + "/$/GetPartitions/" + partitionName + "/$/GetReplicas"
|
||||||
|
res, err := c.getHTTP(basePath, withContinue(continueToken))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var instanceItemsPage InstanceItemsPage
|
||||||
|
err = json.Unmarshal(res, &instanceItemsPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateInstanceItemsPages.Items = append(aggregateInstanceItemsPages.Items, instanceItemsPage.Items...)
|
||||||
|
|
||||||
|
continueToken = getString(instanceItemsPage.ContinuationToken)
|
||||||
|
if continueToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &aggregateInstanceItemsPages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReplicas returns all the replicas associated
|
||||||
|
// with a stateful Service Fabric partition.
|
||||||
|
func (c Client) GetReplicas(appName, serviceName, partitionName string) (*ReplicaItemsPage, error) {
|
||||||
|
var aggregateReplicaItemsPages ReplicaItemsPage
|
||||||
|
var continueToken string
|
||||||
|
for {
|
||||||
|
basePath := "Applications/" + appName + "/$/GetServices/" + serviceName + "/$/GetPartitions/" + partitionName + "/$/GetReplicas"
|
||||||
|
res, err := c.getHTTP(basePath, withContinue(continueToken))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var replicasItemsPage ReplicaItemsPage
|
||||||
|
err = json.Unmarshal(res, &replicasItemsPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateReplicaItemsPages.Items = append(aggregateReplicaItemsPages.Items, replicasItemsPage.Items...)
|
||||||
|
|
||||||
|
continueToken = getString(replicasItemsPage.ContinuationToken)
|
||||||
|
if continueToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &aggregateReplicaItemsPages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServiceExtension returns all the extensions specified
|
||||||
|
// in a Service's manifest file. If the XML schema does not
|
||||||
|
// map to the provided interface, the default type interface will
|
||||||
|
// be returned.
|
||||||
|
func (c Client) GetServiceExtension(appType, applicationVersion, serviceTypeName, extensionKey string, response interface{}) error {
|
||||||
|
res, err := c.getHTTP("ApplicationTypes/"+appType+"/$/GetServiceTypes", withParam("ApplicationTypeVersion", applicationVersion))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error requesting service extensions: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceTypes []ServiceType
|
||||||
|
err = json.Unmarshal(res, &serviceTypes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serviceTypeInfo := range serviceTypes {
|
||||||
|
if serviceTypeInfo.ServiceTypeDescription.ServiceTypeName == serviceTypeName {
|
||||||
|
for _, extension := range serviceTypeInfo.ServiceTypeDescription.Extensions {
|
||||||
|
if strings.EqualFold(extension.Key, extensionKey) {
|
||||||
|
err = xml.Unmarshal([]byte(extension.Value), &response)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not deserialise extension's XML value: %+v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProperties uses the Property Manager API to retrieve
|
||||||
|
// string properties from a name as a dictionary
|
||||||
|
func (c Client) GetProperties(name string) (bool, map[string]string, error) {
|
||||||
|
nameExists, err := c.nameExists(name)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nameExists {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
properties := make(map[string]string)
|
||||||
|
|
||||||
|
var continueToken string
|
||||||
|
for {
|
||||||
|
res, err := c.getHTTP("Names/"+name+"/$/GetProperties", withContinue(continueToken), withParam("IncludeValues", "true"))
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertiesListPage PropertiesListPage
|
||||||
|
err = json.Unmarshal(res, &propertiesListPage)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, fmt.Errorf("could not deserialise JSON response: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, property := range propertiesListPage.Properties {
|
||||||
|
if property.Value.Kind != "String" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
properties[property.Name] = property.Value.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
continueToken = propertiesListPage.ContinuationToken
|
||||||
|
if continueToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, properties, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServiceLabels add labels from service manifest extensions and properties manager
|
||||||
|
// expects extension xml in <Label key="key">value</Label>
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixPeriod := prefix + "."
|
||||||
|
|
||||||
|
labels := map[string]string{}
|
||||||
|
if extensionData.Label != nil {
|
||||||
|
for _, label := range extensionData.Label {
|
||||||
|
if strings.HasPrefix(label.Key, prefixPeriod) {
|
||||||
|
labelKey := strings.Replace(label.Key, prefixPeriod, "", -1)
|
||||||
|
labels[labelKey] = label.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, properties, err := c.GetProperties(service.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
for k, v := range properties {
|
||||||
|
if strings.HasPrefix(k, prefixPeriod) {
|
||||||
|
labelKey := strings.Replace(k, prefixPeriod, "", -1)
|
||||||
|
labels[labelKey] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) nameExists(propertyName string) (bool, error) {
|
||||||
|
res, err := c.getHTTPRaw("Names/" + propertyName)
|
||||||
|
// Get http will return error for any non 200 response code.
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.StatusCode == http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) getHTTP(basePath string, paramsFuncs ...queryParamsFunc) ([]byte, error) {
|
||||||
|
if c.httpClient == nil {
|
||||||
|
return nil, errors.New("invalid http client provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.getURL(basePath, paramsFuncs...)
|
||||||
|
res, err := c.httpClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to Service Fabric server %+v on %s", err, url)
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("Service Fabric responded with error code %s to request %s with body %v", res.Status, url, res.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Body == nil {
|
||||||
|
return nil, errors.New("empty response body from Service Fabric")
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
body, readErr := ioutil.ReadAll(res.Body)
|
||||||
|
if readErr != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response body from Service Fabric response %+v", readErr)
|
||||||
|
}
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) getHTTPRaw(basePath string) (*http.Response, error) {
|
||||||
|
if c.httpClient == nil {
|
||||||
|
return nil, fmt.Errorf("invalid http client provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.getURL(basePath)
|
||||||
|
|
||||||
|
res, err := c.httpClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to Service Fabric server %+v on %s", err, url)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) getURL(basePath string, paramsFuncs ...queryParamsFunc) string {
|
||||||
|
params := []string{"api-version=" + c.apiVersion}
|
||||||
|
|
||||||
|
for _, paramsFunc := range paramsFuncs {
|
||||||
|
params = paramsFunc(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%s?%s", c.endpoint, basePath, strings.Join(params, "&"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getString(str *string) string {
|
||||||
|
if str == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return *str
|
||||||
|
}
|
199
vendor/github.com/jjcollinge/servicefabric/types.go
generated
vendored
Normal file
199
vendor/github.com/jjcollinge/servicefabric/types.go
generated
vendored
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
package servicefabric
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// ApplicationItemsPage encapsulates the paged response
|
||||||
|
// model for Applications in the Service Fabric API
|
||||||
|
type ApplicationItemsPage struct {
|
||||||
|
ContinuationToken *string `json:"ContinuationToken"`
|
||||||
|
Items []ApplicationItem `json:"Items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppParameter Application parameter
|
||||||
|
type AppParameter struct {
|
||||||
|
Key string `json:"Key"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationItem encapsulates the embedded model for
|
||||||
|
// ApplicationItems within the ApplicationItemsPage model
|
||||||
|
type ApplicationItem struct {
|
||||||
|
HealthState string `json:"HealthState"`
|
||||||
|
ID string `json:"Id"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
Parameters []*AppParameter `json:"Parameters"`
|
||||||
|
Status string `json:"Status"`
|
||||||
|
TypeName string `json:"TypeName"`
|
||||||
|
TypeVersion string `json:"TypeVersion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceItemsPage encapsulates the paged response
|
||||||
|
// model for Services in the Service Fabric API
|
||||||
|
type ServiceItemsPage struct {
|
||||||
|
ContinuationToken *string `json:"ContinuationToken"`
|
||||||
|
Items []ServiceItem `json:"Items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceItem encapsulates the embedded model for
|
||||||
|
// ServiceItems within the ServiceItemsPage model
|
||||||
|
type ServiceItem struct {
|
||||||
|
HasPersistedState bool `json:"HasPersistedState"`
|
||||||
|
HealthState string `json:"HealthState"`
|
||||||
|
ID string `json:"Id"`
|
||||||
|
IsServiceGroup bool `json:"IsServiceGroup"`
|
||||||
|
ManifestVersion string `json:"ManifestVersion"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
ServiceKind string `json:"ServiceKind"`
|
||||||
|
ServiceStatus string `json:"ServiceStatus"`
|
||||||
|
TypeName string `json:"TypeName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartitionItemsPage encapsulates the paged response
|
||||||
|
// model for PartitionItems in the Service Fabric API
|
||||||
|
type PartitionItemsPage struct {
|
||||||
|
ContinuationToken *string `json:"ContinuationToken"`
|
||||||
|
Items []PartitionItem `json:"Items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartitionItem encapsulates the service information
|
||||||
|
// returned for each PartitionItem under the service
|
||||||
|
type PartitionItem struct {
|
||||||
|
CurrentConfigurationEpoch ConfigurationEpoch `json:"CurrentConfigurationEpoch"`
|
||||||
|
HealthState string `json:"HealthState"`
|
||||||
|
MinReplicaSetSize int64 `json:"MinReplicaSetSize"`
|
||||||
|
PartitionInformation PartitionInformation `json:"PartitionInformation"`
|
||||||
|
PartitionStatus string `json:"PartitionStatus"`
|
||||||
|
ServiceKind string `json:"ServiceKind"`
|
||||||
|
TargetReplicaSetSize int64 `json:"TargetReplicaSetSize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigurationEpoch Partition configuration epoch
|
||||||
|
type ConfigurationEpoch struct {
|
||||||
|
ConfigurationVersion string `json:"ConfigurationVersion"`
|
||||||
|
DataLossVersion string `json:"DataLossVersion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartitionInformation Partition information
|
||||||
|
type PartitionInformation struct {
|
||||||
|
HighKey string `json:"HighKey"`
|
||||||
|
ID string `json:"Id"`
|
||||||
|
LowKey string `json:"LowKey"`
|
||||||
|
ServicePartitionKind string `json:"ServicePartitionKind"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplicaItemBase shared data used
|
||||||
|
// in both replicas and instances
|
||||||
|
type ReplicaItemBase struct {
|
||||||
|
Address string `json:"Address"`
|
||||||
|
HealthState string `json:"HealthState"`
|
||||||
|
LastInBuildDurationInSeconds string `json:"LastInBuildDurationInSeconds"`
|
||||||
|
NodeName string `json:"NodeName"`
|
||||||
|
ReplicaRole string `json:"ReplicaRole"`
|
||||||
|
ReplicaStatus string `json:"ReplicaStatus"`
|
||||||
|
ServiceKind string `json:"ServiceKind"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplicaItemsPage encapsulates the response
|
||||||
|
// model for Replicas in the Service Fabric API
|
||||||
|
type ReplicaItemsPage struct {
|
||||||
|
ContinuationToken *string `json:"ContinuationToken"`
|
||||||
|
Items []ReplicaItem `json:"Items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplicaItem holds replica specific data
|
||||||
|
type ReplicaItem struct {
|
||||||
|
*ReplicaItemBase
|
||||||
|
ID string `json:"ReplicaId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReplicaData returns replica data
|
||||||
|
func (m *ReplicaItem) GetReplicaData() (string, *ReplicaItemBase) {
|
||||||
|
return m.ID, m.ReplicaItemBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceItemsPage encapsulates the response
|
||||||
|
// model for Instances in the Service Fabric API
|
||||||
|
type InstanceItemsPage struct {
|
||||||
|
ContinuationToken *string `json:"ContinuationToken"`
|
||||||
|
Items []InstanceItem `json:"Items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceItem hold instance specific data
|
||||||
|
type InstanceItem struct {
|
||||||
|
*ReplicaItemBase
|
||||||
|
ID string `json:"InstanceId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReplicaData returns replica data from an instance
|
||||||
|
func (m *InstanceItem) GetReplicaData() (string, *ReplicaItemBase) {
|
||||||
|
return m.ID, m.ReplicaItemBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceType encapsulates the response model for
|
||||||
|
// Service types in the Service Fabric API
|
||||||
|
type ServiceType struct {
|
||||||
|
ServiceTypeDescription ServiceTypeDescription `json:"ServiceTypeDescription"`
|
||||||
|
ServiceManifestVersion string `json:"ServiceManifestVersion"`
|
||||||
|
ServiceManifestName string `json:"ServiceManifestName"`
|
||||||
|
IsServiceGroup bool `json:"IsServiceGroup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceTypeDescription Service Type Description
|
||||||
|
type ServiceTypeDescription struct {
|
||||||
|
IsStateful bool `json:"IsStateful"`
|
||||||
|
ServiceTypeName string `json:"ServiceTypeName"`
|
||||||
|
PlacementConstraints string `json:"PlacementConstraints"`
|
||||||
|
HasPersistedState bool `json:"HasPersistedState"`
|
||||||
|
Kind string `json:"Kind"`
|
||||||
|
Extensions []KeyValuePair `json:"Extensions"`
|
||||||
|
LoadMetrics []interface{} `json:"LoadMetrics"`
|
||||||
|
ServicePlacementPolicies []interface{} `json:"ServicePlacementPolicies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertiesListPage encapsulates the response model for
|
||||||
|
// PagedPropertyInfoList in the Service Fabric API
|
||||||
|
type PropertiesListPage struct {
|
||||||
|
ContinuationToken string `json:"ContinuationToken"`
|
||||||
|
IsConsistent bool `json:"IsConsistent"`
|
||||||
|
Properties []Property `json:"Properties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property Paged Property Info
|
||||||
|
type Property struct {
|
||||||
|
Metadata Metadata `json:"Metadata"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
Value PropValue `json:"Value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata Property Metadata
|
||||||
|
type Metadata struct {
|
||||||
|
CustomTypeID string `json:"CustomTypeId"`
|
||||||
|
LastModifiedUtcTimestamp string `json:"LastModifiedUtcTimestamp"`
|
||||||
|
Parent string `json:"Parent"`
|
||||||
|
SequenceNumber string `json:"SequenceNumber"`
|
||||||
|
SizeInBytes int64 `json:"SizeInBytes"`
|
||||||
|
TypeID string `json:"TypeId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropValue Property value
|
||||||
|
type PropValue struct {
|
||||||
|
Data string `json:"Data"`
|
||||||
|
Kind string `json:"Kind"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyValuePair represents a key value pair structure
|
||||||
|
type KeyValuePair struct {
|
||||||
|
Key string `json:"Key"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceExtensionLabels provides the structure for
|
||||||
|
// deserialising the XML document used to store labels in an Extension
|
||||||
|
type ServiceExtensionLabels struct {
|
||||||
|
XMLName xml.Name `xml:"Labels"`
|
||||||
|
Label []struct {
|
||||||
|
XMLName xml.Name `xml:"Label"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
Key string `xml:"Key,attr"`
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue