Merge branch 'v1.5' into master
This commit is contained in:
commit
4a7297d05c
43 changed files with 667 additions and 198 deletions
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -1,5 +1,32 @@
|
|||
# Change Log
|
||||
|
||||
## [v1.5.0-rc3](https://github.com/containous/traefik/tree/v1.5.0-rc3) (2017-12-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc2...v1.5.0-rc3)
|
||||
|
||||
**Enhancements:**
|
||||
- **[docker,k8s,rancher]** Support regex redirect by frontend ([#2570](https://github.com/containous/traefik/pull/2570) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,docker]** Modify ACME configuration migration into KV store ([#2598](https://github.com/containous/traefik/pull/2598) by [nmengin](https://github.com/nmengin))
|
||||
- **[consulcatalog]** Reload configuration when port change for one service ([#2574](https://github.com/containous/traefik/pull/2574) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Fix bad Træfik update on Consul Catalog ([#2573](https://github.com/containous/traefik/pull/2573) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Add missing entrypoints template. ([#2594](https://github.com/containous/traefik/pull/2594) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Fix stickiness bug due to template syntax error ([#2591](https://github.com/containous/traefik/pull/2591) by [dahefanteng](https://github.com/dahefanteng))
|
||||
- **[marathon]** Update go-marathon ([#2585](https://github.com/containous/traefik/pull/2585) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[mesos]** Mesos: Use slave.PID.Host as task SlaveIP. ([#2590](https://github.com/containous/traefik/pull/2590) by [nemosupremo](https://github.com/nemosupremo))
|
||||
- **[middleware]** Fix RawPath handling in addPrefix ([#2560](https://github.com/containous/traefik/pull/2560) by [risdenk](https://github.com/risdenk))
|
||||
- **[rules]** Add non regex pathPrefix ([#2592](https://github.com/containous/traefik/pull/2592) by [emilevauge](https://github.com/emilevauge))
|
||||
- **[servicefabric]** Fix backend name for Stateful services. (Service Fabric) ([#2559](https://github.com/containous/traefik/pull/2559) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Fix isHealthy logic. ([#2577](https://github.com/containous/traefik/pull/2577) by [ldez](https://github.com/ldez))
|
||||
- **[zk]** Change Zookeeper default prefix. ([#2580](https://github.com/containous/traefik/pull/2580) by [ldez](https://github.com/ldez))
|
||||
- Fix frontend redirect ([#2544](https://github.com/containous/traefik/pull/2544) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Improve documentation for Cloudflare API key ([#2558](https://github.com/containous/traefik/pull/2558) by [mmatur](https://github.com/mmatur))
|
||||
- Move rate limit documentation. ([#2588](https://github.com/containous/traefik/pull/2588) by [ldez](https://github.com/ldez))
|
||||
- Grammar ([#2562](https://github.com/containous/traefik/pull/2562) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- Fix broken links and improve ResponseCodeRatio() description ([#2538](https://github.com/containous/traefik/pull/2538) by [mvasin](https://github.com/mvasin))
|
||||
|
||||
## [v1.5.0-rc2](https://github.com/containous/traefik/tree/v1.5.0-rc2) (2017-12-06)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.5.0-rc2)
|
||||
|
||||
|
|
|
@ -516,6 +516,9 @@ var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend
|
|||
backend = "{{$frontend.Backend}}"
|
||||
priority = {{$frontend.Priority}}
|
||||
passHostHeader = {{$frontend.PassHostHeader}}
|
||||
entryPoints = [{{range $frontend.EntryPoints}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range $frontend.BasicAuth}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
@ -620,7 +623,7 @@ var _templatesKvTmpl = []byte(`{{$frontends := List .Prefix "/frontends/" }}
|
|||
sticky = {{ getSticky . }}
|
||||
{{if hasStickinessLabel $backend}}
|
||||
[backends."{{$backendName}}".loadBalancer.stickiness]
|
||||
cookieName = {{getStickinessCookieName $backend}}
|
||||
cookieName = "{{getStickinessCookieName $backend}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
var defaultZookeeper zk.Provider
|
||||
defaultZookeeper.Watch = true
|
||||
defaultZookeeper.Endpoint = "127.0.0.1:2181"
|
||||
defaultZookeeper.Prefix = "/traefik"
|
||||
defaultZookeeper.Prefix = "traefik"
|
||||
defaultZookeeper.Constraints = types.Constraints{}
|
||||
|
||||
//default Boltdb
|
||||
|
|
|
@ -67,12 +67,21 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
|
|||
return err
|
||||
}
|
||||
}
|
||||
if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
||||
// convert ACME json file to KV store
|
||||
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
object, err := localStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
if traefikConfiguration.GlobalConfiguration.ACME != nil {
|
||||
var object cluster.Object
|
||||
if len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
||||
// convert ACME json file to KV store
|
||||
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
object, err = localStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// Create an empty account to create all the keys into the KV store
|
||||
account := &acme.Account{}
|
||||
account.Init()
|
||||
object = account
|
||||
}
|
||||
|
||||
meta := cluster.NewMetadata(object)
|
||||
|
@ -89,6 +98,11 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Force to delete storagefile
|
||||
err = kv.Delete(kv.Prefix + "/acme/storagefile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -321,35 +321,6 @@ In this example, traffic routed through the first frontend will have the `X-Fram
|
|||
!!! note
|
||||
The detailed documentation for those security headers can be found in [unrolled/secure](https://github.com/unrolled/secure#available-options).
|
||||
|
||||
#### Rate limiting
|
||||
|
||||
Rate limiting can be configured per frontend.
|
||||
Multiple sets of rates can be added to each frontend, but the time periods must be unique.
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
passHostHeader = true
|
||||
entrypoints = ["http"]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/"
|
||||
[frontends.frontend1.ratelimit]
|
||||
extractorfunc = "client.ip"
|
||||
[frontends.frontend1.ratelimit.rateset.rateset1]
|
||||
period = "10s"
|
||||
average = 100
|
||||
burst = 200
|
||||
[frontends.frontend1.ratelimit.rateset.rateset2]
|
||||
period = "3s"
|
||||
average = 5
|
||||
burst = 10
|
||||
```
|
||||
|
||||
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
||||
An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds.
|
||||
These can "burst" up to 10 and 200 in each period respectively.
|
||||
|
||||
### Backends
|
||||
|
||||
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
|
||||
|
|
|
@ -20,6 +20,12 @@ See also [Let's Encrypt examples](/user-guide/examples/#lets-encrypt-support) an
|
|||
#
|
||||
email = "test@traefik.io"
|
||||
|
||||
# File used for certificates storage.
|
||||
#
|
||||
# Optional (Deprecated)
|
||||
#
|
||||
#storageFile = "acme.json"
|
||||
|
||||
# File or key used for certificates storage.
|
||||
#
|
||||
# Required
|
||||
|
@ -55,7 +61,7 @@ entryPoint = "https"
|
|||
#
|
||||
# acmeLogging = true
|
||||
|
||||
# Enable on demand certificate.
|
||||
# Enable on demand certificate. (Deprecated)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
|
@ -89,6 +95,10 @@ entryPoint = "https"
|
|||
# main = "local4.com"
|
||||
```
|
||||
|
||||
!!! note
|
||||
ACME entryPoint has to be relied to the port 443, otherwise ACME Challenges can not be done.
|
||||
It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
### `storage`
|
||||
|
||||
```toml
|
||||
|
@ -100,7 +110,7 @@ storage = "acme.json"
|
|||
|
||||
File or key used for certificates storage.
|
||||
|
||||
**WARNING** If you use Traefik in Docker, you have 2 options:
|
||||
**WARNING** If you use Træfik in Docker, you have 2 options:
|
||||
|
||||
- create a file on your host and mount it as a volume:
|
||||
```toml
|
||||
|
@ -118,6 +128,14 @@ storage = "/etc/traefik/acme/acme.json"
|
|||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
```
|
||||
|
||||
!!! note
|
||||
`storage` replaces `storageFile` which is deprecated.
|
||||
|
||||
!!! note
|
||||
During Træfik configuration migration from a configuration file to a KV store (thanks to `storeconfig` subcommand as described [here](/user-guide/kv-config/#store-configuration-in-key-value-store)), if ACME certificates have to be migrated too, use both `storageFile` and `storage`.
|
||||
`storageFile` will contain the path to the `acme.json` file to migrate.
|
||||
`storage` will contain the key where the certificates will be stored.
|
||||
|
||||
### `dnsProvider`
|
||||
|
||||
```toml
|
||||
|
@ -146,7 +164,7 @@ Select the provider that matches the DNS domain that will host the challenge TXT
|
|||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` |
|
||||
| manual | - | none, but run Traefik interactively & turn on `acmeLogging` to see instructions & press <kbd>Enter</kbd>. |
|
||||
| manual | - | none, but run Træfik interactively & turn on `acmeLogging` to see instructions & press <kbd>Enter</kbd>. |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` |
|
||||
|
@ -171,7 +189,7 @@ If `delayDontCheckDNS` is greater than zero, avoid this & instead just wait so m
|
|||
|
||||
Useful if internal networks block external DNS queries.
|
||||
|
||||
### `onDemand`
|
||||
### `onDemand` (Deprecated)
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
|
@ -188,7 +206,10 @@ This will request a certificate from Let's Encrypt during the first TLS handshak
|
|||
TLS handshakes will be slow when requesting a hostname certificate for the first time, this can lead to DoS attacks.
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits)
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
!!! warning
|
||||
This option is deprecated.
|
||||
|
||||
### `onHostRule`
|
||||
|
||||
|
@ -238,7 +259,7 @@ main = "local4.com"
|
|||
```
|
||||
|
||||
You can provide SANs (alternative domains) to each main domain.
|
||||
All domains must have A/AAAA records pointing to Traefik.
|
||||
All domains must have A/AAAA records pointing to Træfik.
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
|
|
@ -27,9 +27,9 @@ watch = true
|
|||
# Prefix used for KV store.
|
||||
#
|
||||
# Optional
|
||||
# Default: "/traefik"
|
||||
# Default: "traefik"
|
||||
#
|
||||
prefix = "/traefik"
|
||||
prefix = "traefik"
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
|
|
|
@ -277,6 +277,36 @@ Custom error pages are easiest to implement using the file provider.
|
|||
For dynamic providers, the corresponding template file needs to be customized accordingly and referenced in the Traefik configuration.
|
||||
|
||||
|
||||
## Rate limiting
|
||||
|
||||
Rate limiting can be configured per frontend.
|
||||
Multiple sets of rates can be added to each frontend, but the time periods must be unique.
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
passHostHeader = true
|
||||
entrypoints = ["http"]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/"
|
||||
[frontends.frontend1.ratelimit]
|
||||
extractorfunc = "client.ip"
|
||||
[frontends.frontend1.ratelimit.rateset.rateset1]
|
||||
period = "10s"
|
||||
average = 100
|
||||
burst = 200
|
||||
[frontends.frontend1.ratelimit.rateset.rateset2]
|
||||
period = "3s"
|
||||
average = 5
|
||||
burst = 10
|
||||
```
|
||||
|
||||
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
||||
An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds.
|
||||
These can "burst" up to 10 and 200 in each period respectively.
|
||||
|
||||
|
||||
## Retry Configuration
|
||||
|
||||
```toml
|
||||
|
|
|
@ -38,16 +38,7 @@ services:
|
|||
|
||||
etcdctl-ping:
|
||||
image: tenstartups/etcdctl
|
||||
command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storagefile"
|
||||
environment:
|
||||
ETCDCTL_DIAL_: "TIMEOUT 10s"
|
||||
ETCDCTL_API : "3"
|
||||
networks:
|
||||
- net
|
||||
|
||||
etcdctl-rm:
|
||||
image: tenstartups/etcdctl
|
||||
command: --endpoints=[10.0.1.12:2379] del "/traefik/acme/storagefile"
|
||||
command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storage"
|
||||
environment:
|
||||
ETCDCTL_DIAL_: "TIMEOUT 10s"
|
||||
ETCDCTL_API : "3"
|
||||
|
@ -129,7 +120,6 @@ services:
|
|||
image: containous/traefik
|
||||
volumes:
|
||||
- "./traefik.toml:/traefik.toml:ro"
|
||||
- "./acme.json:/acme.json:ro"
|
||||
command: storeconfig --debug
|
||||
networks:
|
||||
- net
|
||||
|
|
|
@ -32,15 +32,6 @@ delete_services() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# Init the environment : get IP address and create needed files
|
||||
init_acme_json() {
|
||||
echo "CREATE empty acme.json file"
|
||||
rm -f $basedir/acme.json && \
|
||||
touch $basedir/acme.json && \
|
||||
echo "{}" > $basedir/acme.json && \
|
||||
chmod 600 $basedir/acme.json # Needed for ACME
|
||||
}
|
||||
|
||||
start_consul() {
|
||||
up_environment consul
|
||||
waiting_counter=12
|
||||
|
@ -76,7 +67,6 @@ start_etcd3() {
|
|||
}
|
||||
|
||||
start_storeconfig_consul() {
|
||||
init_acme_json
|
||||
# Create traefik.toml with consul provider
|
||||
cp $basedir/traefik.toml.tmpl $basedir/traefik.toml
|
||||
echo '
|
||||
|
@ -85,29 +75,13 @@ start_storeconfig_consul() {
|
|||
watch = true
|
||||
prefix = "traefik"' >> $basedir/traefik.toml
|
||||
up_environment traefik-storeconfig
|
||||
rm -f $basedir/traefik.toml && rm -f $basedir/acme.json
|
||||
# Delete acme-storage-file key
|
||||
rm -f $basedir/traefik.toml
|
||||
waiting_counter=5
|
||||
# Not start Traefik store config if consul is not started
|
||||
echo "Delete storage file key..."
|
||||
while [[ -z $(curl -s http://10.0.1.2:8500/v1/kv/traefik/acme/storagefile) && $waiting_counter -gt 0 ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
done
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
echo "[WARN] Unable to get storagefile key in consul"
|
||||
else
|
||||
curl -s --request DELETE http://10.0.1.2:8500/v1/kv/traefik/acme/storagefile
|
||||
ret=$1
|
||||
if [[ $ret -ne 0 ]]; then
|
||||
echo "[ERROR] Unable to delete storagefile key from consul kv."
|
||||
fi
|
||||
fi
|
||||
delete_services traefik-storeconfig
|
||||
|
||||
}
|
||||
|
||||
start_storeconfig_etcd3() {
|
||||
init_acme_json
|
||||
# Create traefik.toml with consul provider
|
||||
cp $basedir/traefik.toml.tmpl $basedir/traefik.toml
|
||||
echo '
|
||||
|
@ -117,20 +91,15 @@ start_storeconfig_etcd3() {
|
|||
prefix = "/traefik"
|
||||
useAPIV3 = true' >> $basedir/traefik.toml
|
||||
up_environment traefik-storeconfig
|
||||
rm -f $basedir/traefik.toml && rm -f $basedir/acme.json
|
||||
# Delete acme-storage-file key
|
||||
rm -f $basedir/traefik.toml
|
||||
waiting_counter=5
|
||||
# Not start Traefik store config if consul is not started
|
||||
# Don't start Traefik store config if ETCD3 is not started
|
||||
echo "Delete storage file key..."
|
||||
while [[ $(docker-compose -f $doc_file up --exit-code-from etcdctl-ping etcdctl-ping &>/dev/null) -ne 0 && $waiting_counter -gt 0 ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
done
|
||||
# Not start Traefik store config if consul is not started
|
||||
echo "Delete storage file key from ETCD3..."
|
||||
|
||||
up_environment etcdctl-rm && \
|
||||
delete_services etcdctl-rm traefik-storeconfig etcdctl-ping
|
||||
delete_services traefik-storeconfig etcdctl-ping
|
||||
}
|
||||
|
||||
start_traefik() {
|
||||
|
|
|
@ -12,7 +12,6 @@ defaultEntryPoints = ["http", "https"]
|
|||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "traefik/acme/account"
|
||||
storageFile = "/acme.json"
|
||||
entryPoint = "https"
|
||||
OnHostRule = true
|
||||
caServer = "http://traefik.boulder.com:4000/directory"
|
||||
|
|
6
glide.lock
generated
6
glide.lock
generated
|
@ -1,4 +1,4 @@
|
|||
hash: f0d5ef854a4c115306c63c15320b595c29f715950eaf5f18418149886ecda400
|
||||
hash: d7f811ac4a011308c6e1f73b618215dee90dae6cace9511f66d4b63d916a337a
|
||||
updated: 2017-12-15T10:34:41.246378337+01:00
|
||||
imports:
|
||||
- name: cloud.google.com/go
|
||||
|
@ -94,7 +94,7 @@ imports:
|
|||
- name: github.com/containous/staert
|
||||
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
||||
- name: github.com/containous/traefik-extra-service-fabric
|
||||
version: c01c1ef60ed612c5e42c1ceae0c6f92e67619cc3
|
||||
version: ca1fb57108293caad285b1c366b763f6c6ab71c9
|
||||
- name: github.com/coreos/bbolt
|
||||
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
||||
- name: github.com/coreos/etcd
|
||||
|
@ -261,7 +261,7 @@ imports:
|
|||
- name: github.com/fatih/color
|
||||
version: 62e9147c64a1ed519147b62a56a14e83e2be02c1
|
||||
- name: github.com/gambol99/go-marathon
|
||||
version: dd6cbd4c2d71294a19fb89158f2a00d427f174ab
|
||||
version: 03b46169666c53b9cc953b875ac5714e5103e064
|
||||
- name: github.com/ghodss/yaml
|
||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||
- name: github.com/go-ini/ini
|
||||
|
|
|
@ -12,7 +12,7 @@ import:
|
|||
- package: github.com/cenk/backoff
|
||||
- package: github.com/containous/flaeg
|
||||
- package: github.com/containous/traefik-extra-service-fabric
|
||||
version: v1.0.4
|
||||
version: v1.0.5
|
||||
- package: github.com/vulcand/oxy
|
||||
version: 7b6e758ab449705195df638765c4ca472248908a
|
||||
repo: https://github.com/containous/oxy.git
|
||||
|
|
|
@ -81,7 +81,7 @@ func taskRecords(st state.State) []state.Task {
|
|||
for _, task := range f.Tasks {
|
||||
for _, slave := range st.Slaves {
|
||||
if task.SlaveID == slave.ID {
|
||||
task.SlaveIP = slave.Hostname
|
||||
task.SlaveIP = slave.PID.Host
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/mesos/mesos-go/upid"
|
||||
"github.com/mesosphere/mesos-dns/records/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -262,6 +263,9 @@ func TestTaskRecords(t *testing.T) {
|
|||
ID: "s_id",
|
||||
Hostname: "127.0.0.1",
|
||||
}
|
||||
slave.PID.UPID = &upid.UPID{}
|
||||
slave.PID.Host = slave.Hostname
|
||||
|
||||
var taskState = state.State{
|
||||
Slaves: []state.Slave{slave},
|
||||
Frameworks: []state.Framework{framework},
|
||||
|
|
|
@ -54,11 +54,32 @@ func (r *Rules) path(paths ...string) *mux.Route {
|
|||
func (r *Rules) pathPrefix(paths ...string) *mux.Route {
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.PathPrefix(strings.TrimSpace(path))
|
||||
buildPath(path, router)
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func buildPath(path string, router *mux.Router) {
|
||||
cleanPath := strings.TrimSpace(path)
|
||||
// {} are used to define a regex pattern in http://www.gorillatoolkit.org/pkg/mux.
|
||||
// if we find a { in the path, that means we use regex, then the gorilla/mux implementation is chosen
|
||||
// otherwise, we use a lightweight implementation
|
||||
if strings.Contains(cleanPath, "{") {
|
||||
router.PathPrefix(cleanPath)
|
||||
} else {
|
||||
m := &prefixMatcher{prefix: cleanPath}
|
||||
router.NewRoute().MatcherFunc(m.Match)
|
||||
}
|
||||
}
|
||||
|
||||
type prefixMatcher struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (m *prefixMatcher) Match(r *http.Request, _ *mux.RouteMatch) bool {
|
||||
return strings.HasPrefix(r.URL.Path, m.prefix) || strings.HasPrefix(r.URL.Path, m.prefix+"/")
|
||||
}
|
||||
|
||||
type bySize []string
|
||||
|
||||
func (a bySize) Len() int { return len(a) }
|
||||
|
@ -111,7 +132,7 @@ func (r *Rules) pathPrefixStrip(paths ...string) *mux.Route {
|
|||
r.route.stripPrefixes = paths
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.PathPrefix(strings.TrimSpace(path))
|
||||
buildPath(path, router)
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
|
|
@ -192,3 +192,67 @@ type fakeHandler struct {
|
|||
}
|
||||
|
||||
func (h *fakeHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}
|
||||
|
||||
func TestPathPrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
path string
|
||||
urls map[string]bool
|
||||
}{
|
||||
{
|
||||
desc: "leading slash",
|
||||
path: "/bar",
|
||||
urls: map[string]bool{
|
||||
"http://foo.com/bar": true,
|
||||
"http://foo.com/bar/": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "leading trailing slash",
|
||||
path: "/bar/",
|
||||
urls: map[string]bool{
|
||||
"http://foo.com/bar": false,
|
||||
"http://foo.com/bar/": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "no slash",
|
||||
path: "bar",
|
||||
urls: map[string]bool{
|
||||
"http://foo.com/bar": false,
|
||||
"http://foo.com/bar/": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "trailing slash",
|
||||
path: "bar/",
|
||||
urls: map[string]bool{
|
||||
"http://foo.com/bar": false,
|
||||
"http://foo.com/bar/": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rls := &Rules{
|
||||
route: &serverRoute{
|
||||
route: &mux.Route{},
|
||||
},
|
||||
}
|
||||
|
||||
rt := rls.pathPrefix(test.path)
|
||||
|
||||
for testURL, expectedMatch := range test.urls {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, testURL, nil)
|
||||
match := rt.Match(req, &mux.RouteMatch{})
|
||||
if match != expectedMatch {
|
||||
t.Errorf("Error matching %s with %s, got %v expected %v", test.path, testURL, match, expectedMatch)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
backend = "{{$frontend.Backend}}"
|
||||
priority = {{$frontend.Priority}}
|
||||
passHostHeader = {{$frontend.PassHostHeader}}
|
||||
entryPoints = [{{range $frontend.EntryPoints}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range $frontend.BasicAuth}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
sticky = {{ getSticky . }}
|
||||
{{if hasStickinessLabel $backend}}
|
||||
[backends."{{$backendName}}".loadBalancer.stickiness]
|
||||
cookieName = {{getStickinessCookieName $backend}}
|
||||
cookieName = "{{getStickinessCookieName $backend}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
|
2
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
2
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
|
@ -303,7 +303,7 @@ func isPrimary(instance replicaInstance) bool {
|
|||
}
|
||||
|
||||
func isHealthy(instanceData *sf.ReplicaItemBase) bool {
|
||||
return instanceData != nil && (instanceData.ReplicaStatus == "Ready" || instanceData.HealthState != "Error")
|
||||
return instanceData != nil && (instanceData.ReplicaStatus == "Ready" && instanceData.HealthState != "Error")
|
||||
}
|
||||
|
||||
func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool {
|
||||
|
|
76
vendor/github.com/gambol99/go-marathon/application.go
generated
vendored
76
vendor/github.com/gambol99/go-marathon/application.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -56,15 +56,16 @@ type Port struct {
|
|||
|
||||
// Application is the definition for an application in marathon
|
||||
type Application struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Cmd *string `json:"cmd,omitempty"`
|
||||
Args *[]string `json:"args,omitempty"`
|
||||
Constraints *[][]string `json:"constraints,omitempty"`
|
||||
Container *Container `json:"container,omitempty"`
|
||||
CPUs float64 `json:"cpus,omitempty"`
|
||||
GPUs *float64 `json:"gpus,omitempty"`
|
||||
Disk *float64 `json:"disk,omitempty"`
|
||||
Env *map[string]string `json:"env,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Cmd *string `json:"cmd,omitempty"`
|
||||
Args *[]string `json:"args,omitempty"`
|
||||
Constraints *[][]string `json:"constraints,omitempty"`
|
||||
Container *Container `json:"container,omitempty"`
|
||||
CPUs float64 `json:"cpus,omitempty"`
|
||||
GPUs *float64 `json:"gpus,omitempty"`
|
||||
Disk *float64 `json:"disk,omitempty"`
|
||||
// Contains non-secret environment variables. Secrets environment variables are part of the Secrets map.
|
||||
Env *map[string]string `json:"-"`
|
||||
Executor *string `json:"executor,omitempty"`
|
||||
HealthChecks *[]HealthCheck `json:"healthChecks,omitempty"`
|
||||
ReadinessChecks *[]ReadinessCheck `json:"readinessChecks,omitempty"`
|
||||
|
@ -99,6 +100,8 @@ type Application struct {
|
|||
LastTaskFailure *LastTaskFailure `json:"lastTaskFailure,omitempty"`
|
||||
Fetch *[]Fetch `json:"fetch,omitempty"`
|
||||
IPAddressPerTask *IPAddressPerTask `json:"ipAddress,omitempty"`
|
||||
Residency *Residency `json:"residency,omitempty"`
|
||||
Secrets *map[string]Secret `json:"-"`
|
||||
}
|
||||
|
||||
// ApplicationVersions is a collection of application versions for a specific app in marathon
|
||||
|
@ -149,6 +152,14 @@ type Stats struct {
|
|||
LifeTime map[string]float64 `json:"lifeTime"`
|
||||
}
|
||||
|
||||
// Secret is the environment variable and secret store path associated with a secret.
|
||||
// The value for EnvVar is populated from the env field, and Source is populated from
|
||||
// the secrets field of the application json.
|
||||
type Secret struct {
|
||||
EnvVar string
|
||||
Source string
|
||||
}
|
||||
|
||||
// SetIPAddressPerTask defines that the application will have a IP address defines by a external agent.
|
||||
// This configuration is not allowed to be used with Port or PortDefinitions. Thus, the implementation
|
||||
// clears both.
|
||||
|
@ -355,8 +366,8 @@ func (r *Application) EmptyLabels() *Application {
|
|||
}
|
||||
|
||||
// AddEnv adds an environment variable to the application
|
||||
// name: the name of the variable
|
||||
// value: go figure, the value associated to the above
|
||||
// name: the name of the variable
|
||||
// value: go figure, the value associated to the above
|
||||
func (r *Application) AddEnv(name, value string) *Application {
|
||||
if r.Env == nil {
|
||||
r.EmptyEnvs()
|
||||
|
@ -375,6 +386,28 @@ func (r *Application) EmptyEnvs() *Application {
|
|||
return r
|
||||
}
|
||||
|
||||
// AddSecret adds a secret declaration
|
||||
// envVar: the name of the environment variable
|
||||
// name: the name of the secret
|
||||
// source: the source ID of the secret
|
||||
func (r *Application) AddSecret(envVar, name, source string) *Application {
|
||||
if r.Secrets == nil {
|
||||
r.EmptySecrets()
|
||||
}
|
||||
(*r.Secrets)[name] = Secret{EnvVar: envVar, Source: source}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// EmptySecrets explicitly empties the secrets -- use this if you need to empty
|
||||
// the secrets of an application that already has secrets set (setting secrets to nil will
|
||||
// keep the current value)
|
||||
func (r *Application) EmptySecrets() *Application {
|
||||
r.Secrets = &map[string]Secret{}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetExecutor sets the executor
|
||||
func (r *Application) SetExecutor(executor string) *Application {
|
||||
r.Executor = &executor
|
||||
|
@ -571,6 +604,23 @@ func (r *Application) EmptyUnreachableStrategy() *Application {
|
|||
return r
|
||||
}
|
||||
|
||||
// SetResidency sets behavior for resident applications, an application is resident when
|
||||
// it has local persistent volumes set
|
||||
func (r *Application) SetResidency(whenLost TaskLostBehaviorType) *Application {
|
||||
r.Residency = &Residency{
|
||||
TaskLostBehavior: whenLost,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// EmptyResidency explicitly empties the residency -- use this if
|
||||
// you need to empty the residency of an application that already has
|
||||
// the residency set (setting it to nil will keep the current value).
|
||||
func (r *Application) EmptyResidency() *Application {
|
||||
r.Residency = &Residency{}
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns the json representation of this application
|
||||
func (r *Application) String() string {
|
||||
s, err := json.MarshalIndent(r, "", " ")
|
||||
|
@ -639,7 +689,7 @@ func (r *marathonClient) ApplicationVersions(name string) (*ApplicationVersions,
|
|||
// name: the id used to identify the application
|
||||
// version: the version (normally a timestamp) you wish to change to
|
||||
func (r *marathonClient) SetApplicationVersion(name string, version *ApplicationVersion) (*DeploymentID, error) {
|
||||
path := fmt.Sprintf(buildPath(name))
|
||||
path := buildPath(name)
|
||||
deploymentID := new(DeploymentID)
|
||||
if err := r.apiPut(path, version, deploymentID); err != nil {
|
||||
return nil, err
|
||||
|
|
106
vendor/github.com/gambol99/go-marathon/application_marshalling.go
generated
vendored
Normal file
106
vendor/github.com/gambol99/go-marathon/application_marshalling.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
Copyright 2017 The go-marathon Authors All rights reserved.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package marathon
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Alias aliases the Application struct so that it will be marshaled/unmarshaled automatically
|
||||
type Alias Application
|
||||
|
||||
// TmpEnvSecret holds the secret values deserialized from the environment variables field
|
||||
type TmpEnvSecret struct {
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
// TmpSecret holds the deserialized secrets field in a Marathon application configuration
|
||||
type TmpSecret struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals the given Application JSON as expected except for environment variables and secrets.
|
||||
// Environment varialbes are stored in the Env field. Secrets, including the environment variable part,
|
||||
// are stored in the Secrets field.
|
||||
func (app *Application) UnmarshalJSON(b []byte) error {
|
||||
aux := &struct {
|
||||
*Alias
|
||||
Env map[string]interface{} `json:"env"`
|
||||
Secrets map[string]TmpSecret `json:"secrets"`
|
||||
}{
|
||||
Alias: (*Alias)(app),
|
||||
}
|
||||
if err := json.Unmarshal(b, aux); err != nil {
|
||||
return fmt.Errorf("malformed application definition %v", err)
|
||||
}
|
||||
env := &map[string]string{}
|
||||
secrets := &map[string]Secret{}
|
||||
|
||||
for envName, genericEnvValue := range aux.Env {
|
||||
switch envValOrSecret := genericEnvValue.(type) {
|
||||
case string:
|
||||
(*env)[envName] = envValOrSecret
|
||||
case map[string]interface{}:
|
||||
for secret, secretStore := range envValOrSecret {
|
||||
if secStore, ok := secretStore.(string); ok && secret == "secret" {
|
||||
(*secrets)[secStore] = Secret{EnvVar: envName}
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("unexpected secret field %v or value type %T", secret, envValOrSecret[secret])
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected environment variable type %T", envValOrSecret)
|
||||
}
|
||||
}
|
||||
app.Env = env
|
||||
for k, v := range aux.Secrets {
|
||||
tmp := (*secrets)[k]
|
||||
tmp.Source = v.Source
|
||||
(*secrets)[k] = tmp
|
||||
}
|
||||
app.Secrets = secrets
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the given Application as expected except for environment variables and secrets,
|
||||
// which are marshaled from specialized structs. The environment variable piece of the secrets and other
|
||||
// normal environment variables are combined and marshaled to the env field. The secrets and the related
|
||||
// source are marshaled into the secrets field.
|
||||
func (app *Application) MarshalJSON() ([]byte, error) {
|
||||
env := make(map[string]interface{})
|
||||
secrets := make(map[string]TmpSecret)
|
||||
|
||||
if app.Env != nil {
|
||||
for k, v := range *app.Env {
|
||||
env[string(k)] = string(v)
|
||||
}
|
||||
}
|
||||
if app.Secrets != nil {
|
||||
for k, v := range *app.Secrets {
|
||||
env[v.EnvVar] = TmpEnvSecret{Secret: k}
|
||||
secrets[k] = TmpSecret{v.Source}
|
||||
}
|
||||
}
|
||||
aux := &struct {
|
||||
*Alias
|
||||
Env map[string]interface{} `json:"env,omitempty"`
|
||||
Secrets map[string]TmpSecret `json:"secrets,omitempty"`
|
||||
}{Alias: (*Alias)(app), Env: env, Secrets: secrets}
|
||||
|
||||
return json.Marshal(aux)
|
||||
}
|
76
vendor/github.com/gambol99/go-marathon/client.go
generated
vendored
76
vendor/github.com/gambol99/go-marathon/client.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -24,6 +24,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
@ -154,6 +155,24 @@ var (
|
|||
ErrMarathonDown = errors.New("all the Marathon hosts are presently down")
|
||||
// ErrTimeoutError is thrown when the operation has timed out
|
||||
ErrTimeoutError = errors.New("the operation has timed out")
|
||||
|
||||
// Default HTTP client used for SSE subscription requests
|
||||
// It is invalid to set client.Timeout because it includes time to read response so
|
||||
// set dial, tls handshake and response header timeouts instead
|
||||
defaultHTTPSSEClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
}).Dial,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// Default HTTP client used for non SSE requests
|
||||
defaultHTTPClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
)
|
||||
|
||||
// EventsChannelContext holds contextual data for an EventsChannel.
|
||||
|
@ -177,8 +196,8 @@ type marathonClient struct {
|
|||
hosts *cluster
|
||||
// a map of service you wish to listen to
|
||||
listeners map[EventsChannel]EventsChannelContext
|
||||
// a custom logger for debug log messages
|
||||
debugLog *log.Logger
|
||||
// a custom log function for debug messages
|
||||
debugLog func(format string, v ...interface{})
|
||||
// the marathon HTTP client to ensure consistency in requests
|
||||
client *httpClient
|
||||
}
|
||||
|
@ -196,9 +215,18 @@ type newRequestError struct {
|
|||
// NewClient creates a new marathon client
|
||||
// config: the configuration to use
|
||||
func NewClient(config Config) (Marathon, error) {
|
||||
// step: if no http client, set to default
|
||||
// step: if the SSE HTTP client is missing, prefer a configured regular
|
||||
// client, and otherwise use the default SSE HTTP client.
|
||||
if config.HTTPSSEClient == nil {
|
||||
config.HTTPSSEClient = defaultHTTPSSEClient
|
||||
if config.HTTPClient != nil {
|
||||
config.HTTPSSEClient = config.HTTPClient
|
||||
}
|
||||
}
|
||||
|
||||
// step: if a regular HTTP client is missing, use the default one.
|
||||
if config.HTTPClient == nil {
|
||||
config.HTTPClient = http.DefaultClient
|
||||
config.HTTPClient = defaultHTTPClient
|
||||
}
|
||||
|
||||
// step: if no polling wait time is set, default to 500 milliseconds.
|
||||
|
@ -215,16 +243,19 @@ func NewClient(config Config) (Marathon, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
debugLogOutput := config.LogOutput
|
||||
if debugLogOutput == nil {
|
||||
debugLogOutput = ioutil.Discard
|
||||
debugLog := func(string, ...interface{}) {}
|
||||
if config.LogOutput != nil {
|
||||
logger := log.New(config.LogOutput, "", 0)
|
||||
debugLog = func(format string, v ...interface{}) {
|
||||
logger.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
return &marathonClient{
|
||||
config: config,
|
||||
listeners: make(map[EventsChannel]EventsChannelContext),
|
||||
hosts: hosts,
|
||||
debugLog: log.New(debugLogOutput, "", 0),
|
||||
debugLog: debugLog,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
@ -280,7 +311,7 @@ func (r *marathonClient) apiCall(method, path string, body, result interface{})
|
|||
if err != nil {
|
||||
r.hosts.markDown(member)
|
||||
// step: attempt the request on another member
|
||||
r.debugLog.Printf("apiCall(): request failed on host: %s, error: %s, trying another\n", member, err)
|
||||
r.debugLog("apiCall(): request failed on host: %s, error: %s, trying another", member, err)
|
||||
continue
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
@ -292,9 +323,9 @@ func (r *marathonClient) apiCall(method, path string, body, result interface{})
|
|||
}
|
||||
|
||||
if len(requestBody) > 0 {
|
||||
r.debugLog.Printf("apiCall(): %v %v %s returned %v %s\n", request.Method, request.URL.String(), requestBody, response.Status, oneLogLine(respBody))
|
||||
r.debugLog("apiCall(): %v %v %s returned %v %s", request.Method, request.URL.String(), requestBody, response.Status, oneLogLine(respBody))
|
||||
} else {
|
||||
r.debugLog.Printf("apiCall(): %v %v returned %v %s\n", request.Method, request.URL.String(), response.Status, oneLogLine(respBody))
|
||||
r.debugLog("apiCall(): %v %v returned %v %s", request.Method, request.URL.String(), response.Status, oneLogLine(respBody))
|
||||
}
|
||||
|
||||
// step: check for a successfull response
|
||||
|
@ -311,7 +342,7 @@ func (r *marathonClient) apiCall(method, path string, body, result interface{})
|
|||
if response.StatusCode >= 500 && response.StatusCode <= 599 {
|
||||
// step: mark the host as down
|
||||
r.hosts.markDown(member)
|
||||
r.debugLog.Printf("apiCall(): request failed, host: %s, status: %d, trying another\n", member, response.StatusCode)
|
||||
r.debugLog("apiCall(): request failed, host: %s, status: %d, trying another", member, response.StatusCode)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -329,16 +360,28 @@ func (r *marathonClient) buildAPIRequest(method, path string, reader io.Reader)
|
|||
}
|
||||
|
||||
// Build the HTTP request to Marathon
|
||||
request, err = r.client.buildMarathonRequest(method, member, path, reader)
|
||||
request, err = r.client.buildMarathonJSONRequest(method, member, path, reader)
|
||||
if err != nil {
|
||||
return nil, member, newRequestError{err}
|
||||
}
|
||||
return request, member, nil
|
||||
}
|
||||
|
||||
// buildMarathonJSONRequest is like buildMarathonRequest but sets the
|
||||
// Content-Type and Accept headers to application/json.
|
||||
func (rc *httpClient) buildMarathonJSONRequest(method, member, path string, reader io.Reader) (request *http.Request, err error) {
|
||||
req, err := rc.buildMarathonRequest(method, member, path, reader)
|
||||
if err == nil {
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Accept", "application/json")
|
||||
}
|
||||
|
||||
return req, err
|
||||
}
|
||||
|
||||
// buildMarathonRequest creates a new HTTP request and configures it according to the *httpClient configuration.
|
||||
// The path must not contain a leading "/", otherwise buildMarathonRequest will panic.
|
||||
func (rc *httpClient) buildMarathonRequest(method string, member string, path string, reader io.Reader) (request *http.Request, err error) {
|
||||
func (rc *httpClient) buildMarathonRequest(method, member, path string, reader io.Reader) (request *http.Request, err error) {
|
||||
if strings.HasPrefix(path, "/") {
|
||||
panic(fmt.Sprintf("Path '%s' must not start with a leading slash", path))
|
||||
}
|
||||
|
@ -361,9 +404,6 @@ func (rc *httpClient) buildMarathonRequest(method string, member string, path st
|
|||
request.Header.Add("Authorization", "token="+rc.config.DCOSToken)
|
||||
}
|
||||
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
request.Header.Add("Accept", "application/json")
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
|
|
23
vendor/github.com/gambol99/go-marathon/cluster.go
generated
vendored
23
vendor/github.com/gambol99/go-marathon/cluster.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -39,6 +39,9 @@ type cluster struct {
|
|||
members []*member
|
||||
// the marathon HTTP client to ensure consistency in requests
|
||||
client *httpClient
|
||||
// healthCheckInterval is the interval by which we probe down nodes for
|
||||
// availability again.
|
||||
healthCheckInterval time.Duration
|
||||
}
|
||||
|
||||
// member represents an individual endpoint
|
||||
|
@ -94,8 +97,9 @@ func newCluster(client *httpClient, marathonURL string, isDCOS bool) (*cluster,
|
|||
}
|
||||
|
||||
return &cluster{
|
||||
client: client,
|
||||
members: members,
|
||||
client: client,
|
||||
members: members,
|
||||
healthCheckInterval: 5 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -130,20 +134,21 @@ func (c *cluster) markDown(endpoint string) {
|
|||
// healthCheckNode performs a health check on the node and when active updates the status
|
||||
func (c *cluster) healthCheckNode(node *member) {
|
||||
// step: wait for the node to become active ... we are assuming a /ping is enough here
|
||||
for {
|
||||
ticker := time.NewTicker(c.healthCheckInterval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
req, err := c.client.buildMarathonRequest("GET", node.endpoint, "ping", nil)
|
||||
if err == nil {
|
||||
res, err := c.client.Do(req)
|
||||
if err == nil && res.StatusCode == 200 {
|
||||
// step: mark the node as active again
|
||||
c.Lock()
|
||||
node.status = memberStatusUp
|
||||
c.Unlock()
|
||||
break
|
||||
}
|
||||
}
|
||||
<-time.After(time.Duration(5 * time.Second))
|
||||
}
|
||||
// step: mark the node as active again
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
node.status = memberStatusUp
|
||||
}
|
||||
|
||||
// activeMembers returns a list of active members
|
||||
|
|
6
vendor/github.com/gambol99/go-marathon/config.go
generated
vendored
6
vendor/github.com/gambol99/go-marathon/config.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -50,8 +50,10 @@ type Config struct {
|
|||
DCOSToken string
|
||||
// LogOutput the output for debug log messages
|
||||
LogOutput io.Writer
|
||||
// HTTPClient is the http client
|
||||
// HTTPClient is the HTTP client
|
||||
HTTPClient *http.Client
|
||||
// HTTPSSEClient is the HTTP client used for SSE subscriptions, can't have client.Timeout set
|
||||
HTTPSSEClient *http.Client
|
||||
// wait time (in milliseconds) between repetitive requests to the API during polling
|
||||
PollingWaitTime time.Duration
|
||||
}
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/const.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/const.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/deployment.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/deployment.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
84
vendor/github.com/gambol99/go-marathon/docker.go
generated
vendored
84
vendor/github.com/gambol99/go-marathon/docker.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -46,10 +46,71 @@ type Parameters struct {
|
|||
|
||||
// Volume is the docker volume details associated to the container
|
||||
type Volume struct {
|
||||
ContainerPath string `json:"containerPath,omitempty"`
|
||||
HostPath string `json:"hostPath,omitempty"`
|
||||
External *ExternalVolume `json:"external,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
ContainerPath string `json:"containerPath,omitempty"`
|
||||
HostPath string `json:"hostPath,omitempty"`
|
||||
External *ExternalVolume `json:"external,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Persistent *PersistentVolume `json:"persistent,omitempty"`
|
||||
}
|
||||
|
||||
type PersistentVolumeType string
|
||||
|
||||
const (
|
||||
PersistentVolumeTypeRoot PersistentVolumeType = "root"
|
||||
PersistentVolumeTypePath PersistentVolumeType = "path"
|
||||
PersistentVolumeTypeMount PersistentVolumeType = "mount"
|
||||
)
|
||||
|
||||
// PersistentVolume declares a Volume to be Persistent, and sets
|
||||
// the size (in MiB) and optional type, max size (MiB) and constraints for the Volume.
|
||||
type PersistentVolume struct {
|
||||
Type PersistentVolumeType `json:"type,omitempty"`
|
||||
Size int `json:"size"`
|
||||
MaxSize int `json:"maxSize,omitempty"`
|
||||
Constraints *[][]string `json:"constraints,omitempty"`
|
||||
}
|
||||
|
||||
// SetType sets the type of mesos disk resource to use
|
||||
// type: PersistentVolumeType enum
|
||||
func (p *PersistentVolume) SetType(tp PersistentVolumeType) *PersistentVolume {
|
||||
p.Type = tp
|
||||
return p
|
||||
}
|
||||
|
||||
// SetSize sets size of the persistent volume
|
||||
// size: size in MiB
|
||||
func (p *PersistentVolume) SetSize(size int) *PersistentVolume {
|
||||
p.Size = size
|
||||
return p
|
||||
}
|
||||
|
||||
// SetMaxSize sets maximum size of an exclusive mount-disk resource to consider;
|
||||
// does not apply to root or path disk resource types
|
||||
// maxSize: size in MiB
|
||||
func (p *PersistentVolume) SetMaxSize(maxSize int) *PersistentVolume {
|
||||
p.MaxSize = maxSize
|
||||
return p
|
||||
}
|
||||
|
||||
// AddConstraint adds a new constraint
|
||||
// constraints: the constraint definition, one constraint per array element
|
||||
func (p *PersistentVolume) AddConstraint(constraints ...string) *PersistentVolume {
|
||||
if p.Constraints == nil {
|
||||
p.EmptyConstraints()
|
||||
}
|
||||
|
||||
c := *p.Constraints
|
||||
c = append(c, constraints)
|
||||
p.Constraints = &c
|
||||
return p
|
||||
}
|
||||
|
||||
// EmptyConstraints explicitly empties constraints -- use this if you need to empty
|
||||
// constraints of an application that already has constraints set (setting constraints to nil will
|
||||
// keep the current value)
|
||||
func (p *PersistentVolume) EmptyConstraints() *PersistentVolume {
|
||||
p.Constraints = &[][]string{}
|
||||
return p
|
||||
}
|
||||
|
||||
// ExternalVolume is an external volume definition
|
||||
|
@ -98,6 +159,19 @@ func (container *Container) EmptyVolumes() *Container {
|
|||
return container
|
||||
}
|
||||
|
||||
// SetPersistentVolume defines persistent properties for volume
|
||||
func (v *Volume) SetPersistentVolume() *PersistentVolume {
|
||||
ev := &PersistentVolume{}
|
||||
v.Persistent = ev
|
||||
return ev
|
||||
}
|
||||
|
||||
// EmptyPersistentVolume empties the persistent volume definition
|
||||
func (v *Volume) EmptyPersistentVolume() *Volume {
|
||||
v.Persistent = &PersistentVolume{}
|
||||
return v
|
||||
}
|
||||
|
||||
// SetExternalVolume define external elements for a volume
|
||||
// name: the name of the volume
|
||||
// provider: the provider of the volume (e.g. dvdi)
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/error.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/error.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2015 Rohith All rights reserved.
|
||||
Copyright 2015 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/events.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/events.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
4
vendor/github.com/gambol99/go-marathon/group.go
generated
vendored
4
vendor/github.com/gambol99/go-marathon/group.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -136,7 +136,7 @@ func (r *marathonClient) GroupBy(name string, opts *GetGroupOpts) (*Group, error
|
|||
// name: the identifier for the group
|
||||
func (r *marathonClient) HasGroup(name string) (bool, error) {
|
||||
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
err := r.apiCall("GET", path, "", nil)
|
||||
err := r.apiGet(path, "", nil)
|
||||
if err != nil {
|
||||
if apiErr, ok := err.(*APIError); ok && apiErr.ErrCode == ErrCodeNotFound {
|
||||
return false, nil
|
||||
|
|
14
vendor/github.com/gambol99/go-marathon/health.go
generated
vendored
14
vendor/github.com/gambol99/go-marathon/health.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -31,37 +31,37 @@ type HealthCheck struct {
|
|||
}
|
||||
|
||||
// SetCommand sets the given command on the health check.
|
||||
func (h HealthCheck) SetCommand(c Command) HealthCheck {
|
||||
func (h *HealthCheck) SetCommand(c Command) *HealthCheck {
|
||||
h.Command = &c
|
||||
return h
|
||||
}
|
||||
|
||||
// SetPortIndex sets the given port index on the health check.
|
||||
func (h HealthCheck) SetPortIndex(i int) HealthCheck {
|
||||
func (h *HealthCheck) SetPortIndex(i int) *HealthCheck {
|
||||
h.PortIndex = &i
|
||||
return h
|
||||
}
|
||||
|
||||
// SetPort sets the given port on the health check.
|
||||
func (h HealthCheck) SetPort(i int) HealthCheck {
|
||||
func (h *HealthCheck) SetPort(i int) *HealthCheck {
|
||||
h.Port = &i
|
||||
return h
|
||||
}
|
||||
|
||||
// SetPath sets the given path on the health check.
|
||||
func (h HealthCheck) SetPath(p string) HealthCheck {
|
||||
func (h *HealthCheck) SetPath(p string) *HealthCheck {
|
||||
h.Path = &p
|
||||
return h
|
||||
}
|
||||
|
||||
// SetMaxConsecutiveFailures sets the maximum consecutive failures on the health check.
|
||||
func (h HealthCheck) SetMaxConsecutiveFailures(i int) HealthCheck {
|
||||
func (h *HealthCheck) SetMaxConsecutiveFailures(i int) *HealthCheck {
|
||||
h.MaxConsecutiveFailures = &i
|
||||
return h
|
||||
}
|
||||
|
||||
// SetIgnoreHTTP1xx sets ignore http 1xx on the health check.
|
||||
func (h HealthCheck) SetIgnoreHTTP1xx(ignore bool) HealthCheck {
|
||||
func (h *HealthCheck) SetIgnoreHTTP1xx(ignore bool) *HealthCheck {
|
||||
h.IgnoreHTTP1xx = &ignore
|
||||
return h
|
||||
}
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/info.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/info.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/last_task_failure.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/last_task_failure.go
generated
vendored
|
@ -1,4 +1,5 @@
|
|||
/*
|
||||
Copyright 2015 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -20,6 +21,7 @@ type LastTaskFailure struct {
|
|||
AppID string `json:"appId,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
SlaveID string `json:"slaveId,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
TaskID string `json:"taskId,omitempty"`
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
|
|
30
vendor/github.com/gambol99/go-marathon/port_definition.go
generated
vendored
30
vendor/github.com/gambol99/go-marathon/port_definition.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016 Rohith All rights reserved.
|
||||
Copyright 2016 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -27,15 +27,39 @@ type PortDefinition struct {
|
|||
}
|
||||
|
||||
// SetPort sets the given port for the PortDefinition
|
||||
func (p PortDefinition) SetPort(port int) PortDefinition {
|
||||
func (p *PortDefinition) SetPort(port int) *PortDefinition {
|
||||
if p.Port == nil {
|
||||
p.EmptyPort()
|
||||
}
|
||||
p.Port = &port
|
||||
return p
|
||||
}
|
||||
|
||||
// EmptyPort sets the port to 0 for the PortDefinition
|
||||
func (p *PortDefinition) EmptyPort() *PortDefinition {
|
||||
port := 0
|
||||
p.Port = &port
|
||||
return p
|
||||
}
|
||||
|
||||
// SetProtocol sets the protocol for the PortDefinition
|
||||
// protocol: the protocol as a string
|
||||
func (p *PortDefinition) SetProtocol(protocol string) *PortDefinition {
|
||||
p.Protocol = protocol
|
||||
return p
|
||||
}
|
||||
|
||||
// SetName sets the name for the PortDefinition
|
||||
// name: the name of the PortDefinition
|
||||
func (p *PortDefinition) SetName(name string) *PortDefinition {
|
||||
p.Name = name
|
||||
return p
|
||||
}
|
||||
|
||||
// AddLabel adds a label to the PortDefinition
|
||||
// name: the name of the label
|
||||
// value: value for this label
|
||||
func (p PortDefinition) AddLabel(name, value string) PortDefinition {
|
||||
func (p *PortDefinition) AddLabel(name, value string) *PortDefinition {
|
||||
if p.Labels == nil {
|
||||
p.EmptyLabels()
|
||||
}
|
||||
|
|
8
vendor/github.com/gambol99/go-marathon/queue.go
generated
vendored
8
vendor/github.com/gambol99/go-marathon/queue.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016 Rohith All rights reserved.
|
||||
Copyright 2016 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -52,9 +52,5 @@ func (r *marathonClient) Queue() (*Queue, error) {
|
|||
// appID: the ID of the application
|
||||
func (r *marathonClient) DeleteQueueDelay(appID string) error {
|
||||
path := fmt.Sprintf("%s/%s/delay", marathonAPIQueue, trimRootPath(appID))
|
||||
err := r.apiDelete(path, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return r.apiDelete(path, nil, nil)
|
||||
}
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/readiness.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/readiness.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017 Rohith All rights reserved.
|
||||
Copyright 2017 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
48
vendor/github.com/gambol99/go-marathon/residency.go
generated
vendored
Normal file
48
vendor/github.com/gambol99/go-marathon/residency.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2017 The go-marathon Authors All rights reserved.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package marathon
|
||||
|
||||
import "time"
|
||||
|
||||
// TaskLostBehaviorType sets action taken when the resident task is lost
|
||||
type TaskLostBehaviorType string
|
||||
|
||||
const (
|
||||
// TaskLostBehaviorTypeWaitForever indicates to not take any action when the resident task is lost
|
||||
TaskLostBehaviorTypeWaitForever TaskLostBehaviorType = "WAIT_FOREVER"
|
||||
// TaskLostBehaviorTypeWaitForever indicates to try relaunching the lost resident task on
|
||||
// another node after the relaunch escalation timeout has elapsed
|
||||
TaskLostBehaviorTypeRelaunchAfterTimeout TaskLostBehaviorType = "RELAUNCH_AFTER_TIMEOUT"
|
||||
)
|
||||
|
||||
// Residency defines how terminal states of tasks with local persistent volumes are handled
|
||||
type Residency struct {
|
||||
TaskLostBehavior TaskLostBehaviorType `json:"taskLostBehavior,omitempty"`
|
||||
RelaunchEscalationTimeoutSeconds int `json:"relaunchEscalationTimeoutSeconds,omitempty"`
|
||||
}
|
||||
|
||||
// SetTaskLostBehavior sets the residency behavior
|
||||
func (r *Residency) SetTaskLostBehavior(behavior TaskLostBehaviorType) *Residency {
|
||||
r.TaskLostBehavior = behavior
|
||||
return r
|
||||
}
|
||||
|
||||
// SetRelaunchEscalationTimeout sets the residency relaunch escalation timeout with seconds precision
|
||||
func (r *Residency) SetRelaunchEscalationTimeout(timeout time.Duration) *Residency {
|
||||
r.RelaunchEscalationTimeoutSeconds = int(timeout.Seconds())
|
||||
return r
|
||||
}
|
38
vendor/github.com/gambol99/go-marathon/subscription.go
generated
vendored
38
vendor/github.com/gambol99/go-marathon/subscription.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -103,8 +103,7 @@ func (r *marathonClient) registerSubscription() error {
|
|||
case EventsTransportCallback:
|
||||
return r.registerCallbackSubscription()
|
||||
case EventsTransportSSE:
|
||||
r.registerSSESubscription()
|
||||
return nil
|
||||
return r.registerSSESubscription()
|
||||
default:
|
||||
return fmt.Errorf("the events transport: %d is not supported", r.config.EventsTransport)
|
||||
}
|
||||
|
@ -167,27 +166,34 @@ func (r *marathonClient) registerCallbackSubscription() error {
|
|||
// connect to the SSE stream and to process the received events. To establish
|
||||
// the connection it tries the active cluster members until no more member is
|
||||
// active. When this happens it will retry to get a connection every 5 seconds.
|
||||
func (r *marathonClient) registerSSESubscription() {
|
||||
func (r *marathonClient) registerSSESubscription() error {
|
||||
if r.subscribedToSSE {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.config.HTTPSSEClient.Timeout != 0 {
|
||||
return fmt.Errorf(
|
||||
"global timeout must not be set for SSE connections (found %s) -- remove global timeout from HTTP client or provide separate SSE HTTP client without global timeout",
|
||||
r.config.HTTPSSEClient.Timeout,
|
||||
)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
stream, err := r.connectToSSE()
|
||||
if err != nil {
|
||||
r.debugLog.Printf("Error connecting SSE subscription: %s", err)
|
||||
r.debugLog("Error connecting SSE subscription: %s", err)
|
||||
<-time.After(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
err = r.listenToSSE(stream)
|
||||
stream.Close()
|
||||
r.debugLog.Printf("Error on SSE subscription: %s", err)
|
||||
r.debugLog("Error on SSE subscription: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
r.subscribedToSSE = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// connectToSSE tries to establish an *eventsource.Stream to any of the Marathon cluster members, marking the
|
||||
|
@ -209,15 +215,15 @@ func (r *marathonClient) connectToSSE() (*eventsource.Stream, error) {
|
|||
// its underlying fields for performance reasons. See note that at least the Transport
|
||||
// should be reused here: https://golang.org/pkg/net/http/#Client
|
||||
httpClient := &http.Client{
|
||||
Transport: r.config.HTTPClient.Transport,
|
||||
CheckRedirect: r.config.HTTPClient.CheckRedirect,
|
||||
Jar: r.config.HTTPClient.Jar,
|
||||
Timeout: r.config.HTTPClient.Timeout,
|
||||
Transport: r.config.HTTPSSEClient.Transport,
|
||||
CheckRedirect: r.config.HTTPSSEClient.CheckRedirect,
|
||||
Jar: r.config.HTTPSSEClient.Jar,
|
||||
Timeout: r.config.HTTPSSEClient.Timeout,
|
||||
}
|
||||
|
||||
stream, err := eventsource.SubscribeWith("", httpClient, request)
|
||||
if err != nil {
|
||||
r.debugLog.Printf("Error subscribing to Marathon event stream: %s", err)
|
||||
r.debugLog("Error subscribing to Marathon event stream: %s", err)
|
||||
r.hosts.markDown(member)
|
||||
continue
|
||||
}
|
||||
|
@ -231,7 +237,7 @@ func (r *marathonClient) listenToSSE(stream *eventsource.Stream) error {
|
|||
select {
|
||||
case ev := <-stream.Events:
|
||||
if err := r.handleEvent(ev.Data()); err != nil {
|
||||
r.debugLog.Printf("listenToSSE(): failed to handle event: %v", err)
|
||||
r.debugLog("listenToSSE(): failed to handle event: %v", err)
|
||||
}
|
||||
case err := <-stream.Errors:
|
||||
return err
|
||||
|
@ -319,12 +325,12 @@ func (r *marathonClient) handleCallbackEvent(writer http.ResponseWriter, request
|
|||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
// TODO should this return a 500?
|
||||
r.debugLog.Printf("handleCallbackEvent(): failed to read request body, error: %s\n", err)
|
||||
r.debugLog("handleCallbackEvent(): failed to read request body, error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.handleEvent(string(body[:])); err != nil {
|
||||
// TODO should this return a 500?
|
||||
r.debugLog.Printf("handleCallbackEvent(): failed to handle event: %v\n", err)
|
||||
r.debugLog("handleCallbackEvent(): failed to handle event: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
4
vendor/github.com/gambol99/go-marathon/task.go
generated
vendored
4
vendor/github.com/gambol99/go-marathon/task.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -217,7 +217,7 @@ func (r *Task) allHealthChecksAlive() bool {
|
|||
}
|
||||
// step: check the health results then
|
||||
for _, check := range r.HealthCheckResults {
|
||||
if check.Alive == false {
|
||||
if !check.Alive {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
6
vendor/github.com/gambol99/go-marathon/unreachable_strategy.go
generated
vendored
6
vendor/github.com/gambol99/go-marathon/unreachable_strategy.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017 Rohith All rights reserved.
|
||||
Copyright 2017 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -65,13 +65,13 @@ func (us *UnreachableStrategy) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
|
||||
// SetInactiveAfterSeconds sets the period after which instance will be marked as inactive.
|
||||
func (us UnreachableStrategy) SetInactiveAfterSeconds(cap float64) UnreachableStrategy {
|
||||
func (us *UnreachableStrategy) SetInactiveAfterSeconds(cap float64) *UnreachableStrategy {
|
||||
us.InactiveAfterSeconds = &cap
|
||||
return us
|
||||
}
|
||||
|
||||
// SetExpungeAfterSeconds sets the period after which instance will be expunged.
|
||||
func (us UnreachableStrategy) SetExpungeAfterSeconds(cap float64) UnreachableStrategy {
|
||||
func (us *UnreachableStrategy) SetExpungeAfterSeconds(cap float64) *UnreachableStrategy {
|
||||
us.ExpungeAfterSeconds = &cap
|
||||
return us
|
||||
}
|
||||
|
|
6
vendor/github.com/gambol99/go-marathon/upgrade_strategy.go
generated
vendored
6
vendor/github.com/gambol99/go-marathon/upgrade_strategy.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -23,13 +23,13 @@ type UpgradeStrategy struct {
|
|||
}
|
||||
|
||||
// SetMinimumHealthCapacity sets the minimum health capacity.
|
||||
func (us UpgradeStrategy) SetMinimumHealthCapacity(cap float64) UpgradeStrategy {
|
||||
func (us *UpgradeStrategy) SetMinimumHealthCapacity(cap float64) *UpgradeStrategy {
|
||||
us.MinimumHealthCapacity = &cap
|
||||
return us
|
||||
}
|
||||
|
||||
// SetMaximumOverCapacity sets the maximum over capacity.
|
||||
func (us UpgradeStrategy) SetMaximumOverCapacity(cap float64) UpgradeStrategy {
|
||||
func (us *UpgradeStrategy) SetMaximumOverCapacity(cap float64) *UpgradeStrategy {
|
||||
us.MaximumOverCapacity = &cap
|
||||
return us
|
||||
}
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/utils.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/utils.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2014 Rohith All rights reserved.
|
||||
Copyright 2014 The go-marathon Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
Loading…
Reference in a new issue