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"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik-extra-service-fabric"
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares/accesslog"
|
||||
|
@ -23,6 +24,7 @@ import (
|
|||
"github.com/containous/traefik/provider/rest"
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
"github.com/containous/traefik/types"
|
||||
sf "github.com/jjcollinge/servicefabric"
|
||||
)
|
||||
|
||||
// TraefikConfiguration holds GlobalConfiguration and other stuff
|
||||
|
@ -163,6 +165,11 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
var defaultEureka eureka.Provider
|
||||
defaultEureka.Delay = "30s"
|
||||
|
||||
// default ServiceFabric
|
||||
var defaultServiceFabric servicefabric.Provider
|
||||
defaultServiceFabric.APIVersion = sf.DefaultAPIVersion
|
||||
defaultServiceFabric.RefreshSeconds = 10
|
||||
|
||||
// default Ping
|
||||
var defaultPing = ping.Handler{
|
||||
EntryPoint: "traefik",
|
||||
|
@ -196,7 +203,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
}
|
||||
|
||||
// default LifeCycle
|
||||
defaultLifeycle := configuration.LifeCycle{
|
||||
defaultLifeCycle := configuration.LifeCycle{
|
||||
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||
}
|
||||
|
||||
|
@ -252,7 +259,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
ForwardingTimeouts: &forwardingTimeouts,
|
||||
TraefikLog: &defaultTraefikLog,
|
||||
AccessLog: &defaultAccessLog,
|
||||
LifeCycle: &defaultLifeycle,
|
||||
LifeCycle: &defaultLifeCycle,
|
||||
Ping: &defaultPing,
|
||||
API: &defaultAPI,
|
||||
Metrics: &defaultMetrics,
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik-extra-service-fabric"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/log"
|
||||
|
@ -88,6 +89,7 @@ type GlobalConfiguration struct {
|
|||
ECS *ecs.Provider `description:"Enable ECS 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"`
|
||||
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"`
|
||||
API *api.Handler `description:"Enable api/dashboard" 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
|
||||
updated: 2017-11-21T21:24:24.601164724+01:00
|
||||
hash: e17ccb5f089116a333c1d2ccba9f5d710bd28779b7d4461e37a1c301c2a00fba
|
||||
updated: 2017-11-22T15:59:09.002650953+01:00
|
||||
imports:
|
||||
- name: cloud.google.com/go
|
||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||
|
@ -93,6 +93,8 @@ imports:
|
|||
version: 06ccd3e75091eb659b1d720cda0e16bc7057954c
|
||||
- name: github.com/containous/staert
|
||||
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
||||
- name: github.com/containous/traefik-extra-service-fabric
|
||||
version: 43b41061088161b93413eb35bd1496f11347c43b
|
||||
- name: github.com/coreos/bbolt
|
||||
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
||||
- name: github.com/coreos/etcd
|
||||
|
@ -334,7 +336,7 @@ imports:
|
|||
- name: github.com/huandu/xstrings
|
||||
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
||||
- name: github.com/imdario/mergo
|
||||
version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc
|
||||
version: 7fe0c75c13abdee74b09fcacef5ea1c6bba6a874
|
||||
- name: github.com/influxdata/influxdb
|
||||
version: 2d474a3089bcfce6b472779be9470a1f0ef3d5e4
|
||||
subpackages:
|
||||
|
@ -345,6 +347,8 @@ imports:
|
|||
version: 2fd0705ce648e602e6c9c57329a174270a4f6688
|
||||
subpackages:
|
||||
- lib
|
||||
- name: github.com/jjcollinge/servicefabric
|
||||
version: 93a44e59fc887cda489913c6fc5bda834989f3bd
|
||||
- name: github.com/jmespath/go-jmespath
|
||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
- name: github.com/jonboulle/clockwork
|
||||
|
@ -368,7 +372,7 @@ imports:
|
|||
- name: github.com/Masterminds/semver
|
||||
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||
- name: github.com/Masterminds/sprig
|
||||
version: 9526be0327b26ad31aa70296a7b10704883976d5
|
||||
version: e039e20e500c2c025d9145be375e27cf42a94174
|
||||
- name: github.com/mattn/go-colorable
|
||||
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
||||
repo: https://github.com/mattn/go-colorable
|
||||
|
|
|
@ -11,6 +11,8 @@ import:
|
|||
version: 10f801ebc38b33738c9d17d50860f484a0988ff5
|
||||
- package: github.com/cenk/backoff
|
||||
- package: github.com/containous/flaeg
|
||||
- package: github.com/containous/traefik-extra-service-fabric
|
||||
version: ^v1.0.0
|
||||
- package: github.com/vulcand/oxy
|
||||
version: 7b6e758ab449705195df638765c4ca472248908a
|
||||
repo: https://github.com/containous/oxy.git
|
||||
|
|
|
@ -85,6 +85,7 @@ pages:
|
|||
- 'Backend: Mesos': 'configuration/backends/mesos.md'
|
||||
- 'Backend: Rancher': 'configuration/backends/rancher.md'
|
||||
- 'Backend: Rest': 'configuration/backends/rest.md'
|
||||
- 'Backend: Service Fabric': 'configuration/backends/servicefabric.md'
|
||||
- 'Backend: Zookeeper': 'configuration/backends/zookeeper.md'
|
||||
- 'API / Dashboard': 'configuration/api.md'
|
||||
- 'Ping': 'configuration/ping.md'
|
||||
|
|
|
@ -559,6 +559,9 @@ func (s *Server) configureProviders() {
|
|||
if s.globalConfiguration.DynamoDB != nil {
|
||||
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() {
|
||||
|
|
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,
|
||||
"max": max,
|
||||
"min": min,
|
||||
"ceil": ceil,
|
||||
"floor": floor,
|
||||
"round": round,
|
||||
|
||||
// string slices. Note that we reverse the order b/c that's better
|
||||
// for template processing.
|
||||
|
@ -258,4 +261,12 @@ var genericMap = map[string]interface{}{
|
|||
|
||||
// Flow Control:
|
||||
"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
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if dst.IsNil() {
|
||||
v := reflect.New(dst.Type().Elem())
|
||||
dst.Set(v)
|
||||
}
|
||||
dst = dst.Elem()
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
srcMap := src.Interface().(map[string]interface{})
|
||||
for key := range srcMap {
|
||||
|
@ -85,6 +92,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
|
|||
srcKind = reflect.Ptr
|
||||
}
|
||||
}
|
||||
|
||||
if !srcElement.IsValid() {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
} 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)
|
||||
} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
|
||||
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||
return
|
||||
}
|
||||
} 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"
|
||||
)
|
||||
|
||||
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.
|
||||
// The map argument tracks comparisons that have already been seen, which allows
|
||||
// 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() {
|
||||
case reflect.Struct:
|
||||
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
|
||||
return
|
||||
if hasExportedField(dst) {
|
||||
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||
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:
|
||||
if len(src.MapKeys()) == 0 && !src.IsNil() && len(dst.MapKeys()) == 0 {
|
||||
dst.Set(reflect.MakeMap(dst.Type()))
|
||||
return
|
||||
}
|
||||
for _, key := range src.MapKeys() {
|
||||
srcElement := src.MapIndex(key)
|
||||
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 dst.IsNil() {
|
||||
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:
|
||||
fallthrough
|
||||
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() {
|
||||
break
|
||||
} else if dst.IsNil() {
|
||||
} else if dst.IsNil() || overwrite {
|
||||
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
||||
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
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
case reflect.Interface, reflect.Ptr, reflect.Func:
|
||||
return v.IsNil()
|
||||
}
|
||||
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