Merge branch 'v1.3' into merge-back-1_3_0-rc3
This commit is contained in:
commit
2833d68f15
17 changed files with 357 additions and 195 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -1,5 +1,24 @@
|
|||
# Change Log
|
||||
|
||||
## [v1.3.0-rc3](https://github.com/containous/traefik/tree/v1.3.0-rc3) (2017-05-24)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc2...v1.3.0-rc3)
|
||||
|
||||
**Enhancements:**
|
||||
- [#1658](https://api.github.com/repos/containous/traefik/issues/1658) Get testify/require dependency. ([timoreimann](https://api.github.com/users/timoreimann))
|
||||
|
||||
**Bug fixes:**
|
||||
- [#1507](https://api.github.com/repos/containous/traefik/issues/1507) Create log folder if not present ([tanyadegurechaff](https://api.github.com/users/tanyadegurechaff))
|
||||
- [#1604](https://api.github.com/repos/containous/traefik/issues/1604) [k8s] Ignore Ingresses with empty Endpoint subsets. ([timoreimann](https://api.github.com/users/timoreimann))
|
||||
- [#1630](https://api.github.com/repos/containous/traefik/issues/1630) [k8s] Remove rule type path list. ([timoreimann](https://api.github.com/users/timoreimann))
|
||||
- [#1635](https://api.github.com/repos/containous/traefik/issues/1635) Upgrade go-marathon to 15ea23e. ([timoreimann](https://api.github.com/users/timoreimann))
|
||||
- [#1638](https://api.github.com/repos/containous/traefik/issues/1638) Fix behavior for PathPrefixStrip ([seryl](https://api.github.com/users/seryl))
|
||||
- [#1654](https://api.github.com/repos/containous/traefik/issues/1654) fix: Empty Rancher Service Labels. ([ldez](https://api.github.com/users/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- [#1578](https://api.github.com/repos/containous/traefik/issues/1578) Add Marathon guide. ([Stibbons](https://api.github.com/users/Stibbons))
|
||||
- [#1602](https://api.github.com/repos/containous/traefik/issues/1602) Re Orginise k8s docs to make 1.6 usage easier ([errm](https://api.github.com/users/errm))
|
||||
- [#1642](https://api.github.com/repos/containous/traefik/issues/1642) Update changelog ([ldez](https://api.github.com/users/ldez))
|
||||
|
||||
## [v1.3.0-rc2](https://github.com/containous/traefik/tree/v1.3.0-rc2) (2017-05-16)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.3.0-rc2)
|
||||
|
||||
|
|
4
glide.lock
generated
4
glide.lock
generated
|
@ -1,5 +1,5 @@
|
|||
hash: e59e8244152a823cd3633fb09cdd583c4e5be78d7b50fb7047ba6b6a9ed5e8ec
|
||||
updated: 2017-05-02T11:46:23.91434995-04:00
|
||||
updated: 2017-05-19T23:30:19.890844996+02:00
|
||||
imports:
|
||||
- name: cloud.google.com/go
|
||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||
|
@ -201,7 +201,7 @@ imports:
|
|||
- name: github.com/fatih/color
|
||||
version: 9131ab34cf20d2f6d83fdc67168a5430d1c7dc23
|
||||
- name: github.com/gambol99/go-marathon
|
||||
version: d672c6fbb499596869d95146a26e7d0746c06c54
|
||||
version: 15ea23e360abb8b25071e677aed344f31838e403
|
||||
- name: github.com/ghodss/yaml
|
||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||
- name: github.com/go-ini/ini
|
||||
|
|
|
@ -17,17 +17,29 @@ type StripPrefix struct {
|
|||
|
||||
func (s *StripPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
for _, prefix := range s.Prefixes {
|
||||
if p := strings.TrimPrefix(r.URL.Path, strings.TrimSpace(prefix)); len(p) < len(r.URL.Path) {
|
||||
r.URL.Path = p
|
||||
r.Header[forwardedPrefixHeader] = []string{prefix}
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
origPrefix := strings.TrimSpace(prefix)
|
||||
if origPrefix == r.URL.Path {
|
||||
r.URL.Path = "/"
|
||||
s.serveRequest(w, r, origPrefix)
|
||||
return
|
||||
}
|
||||
|
||||
prefix = strings.TrimSuffix(origPrefix, "/") + "/"
|
||||
if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
|
||||
r.URL.Path = "/" + strings.TrimPrefix(p, "/")
|
||||
s.serveRequest(w, r, origPrefix)
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
func (s *StripPrefix) serveRequest(w http.ResponseWriter, r *http.Request, prefix string) {
|
||||
r.Header[forwardedPrefixHeader] = []string{prefix}
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// SetHandler sets handler
|
||||
func (s *StripPrefix) SetHandler(Handler http.Handler) {
|
||||
s.Handler = Handler
|
||||
|
|
103
middlewares/stripPrefix_test.go
Normal file
103
middlewares/stripPrefix_test.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStripPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
prefixes []string
|
||||
path string
|
||||
expectedStatusCode int
|
||||
expectedPath string
|
||||
}{
|
||||
{
|
||||
desc: "no prefixes configured",
|
||||
prefixes: []string{},
|
||||
path: "/noprefixes",
|
||||
expectedStatusCode: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "wildcard (.*) requests",
|
||||
prefixes: []string{"/"},
|
||||
path: "/",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/",
|
||||
},
|
||||
{
|
||||
desc: "prefix and path matching",
|
||||
prefixes: []string{"/stat"},
|
||||
path: "/stat",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/",
|
||||
},
|
||||
{
|
||||
desc: "path prefix on exactly matching path",
|
||||
prefixes: []string{"/stat/"},
|
||||
path: "/stat/",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/",
|
||||
},
|
||||
{
|
||||
desc: "path prefix on matching longer path",
|
||||
prefixes: []string{"/stat/"},
|
||||
path: "/stat/us",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/us",
|
||||
},
|
||||
{
|
||||
desc: "path prefix on mismatching path",
|
||||
prefixes: []string{"/stat/"},
|
||||
path: "/status",
|
||||
expectedStatusCode: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "general prefix on matching path",
|
||||
prefixes: []string{"/stat"},
|
||||
path: "/stat/",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/",
|
||||
},
|
||||
{
|
||||
desc: "earlier prefix matching",
|
||||
prefixes: []string{"/stat", "/stat/us"},
|
||||
path: "/stat/us",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/us",
|
||||
},
|
||||
{
|
||||
desc: "later prefix matching",
|
||||
prefixes: []string{"/mismatch", "/stat"},
|
||||
path: "/stat",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedPath: "/",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var gotPath string
|
||||
server := httptest.NewServer(&StripPrefix{
|
||||
Prefixes: test.prefixes,
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotPath = r.URL.Path
|
||||
}),
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
resp, err := http.Get(server.URL + test.path)
|
||||
require.NoError(t, err, "Failed to send GET request")
|
||||
assert.Equal(t, test.expectedStatusCode, resp.StatusCode, "Unexpected status code")
|
||||
|
||||
assert.Equal(t, test.expectedPath, gotPath, "Unexpected path")
|
||||
})
|
||||
}
|
||||
}
|
|
@ -27,9 +27,6 @@ var _ provider.Provider = (*Provider)(nil)
|
|||
|
||||
const (
|
||||
annotationFrontendRuleType = "traefik.frontend.rule.type"
|
||||
ruleTypePathPrefixStrip = "PathPrefixStrip"
|
||||
ruleTypePathStrip = "PathStrip"
|
||||
ruleTypePath = "Path"
|
||||
ruleTypePathPrefix = "PathPrefix"
|
||||
|
||||
annotationKubernetesWhitelistSourceRange = "ingress.kubernetes.io/whitelist-source-range"
|
||||
|
@ -205,12 +202,8 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
|||
}
|
||||
|
||||
if len(pa.Path) > 0 {
|
||||
ruleType, unknown := getRuleTypeFromAnnotation(i.Annotations)
|
||||
switch {
|
||||
case unknown:
|
||||
log.Warnf("Unknown RuleType '%s' for Ingress %s/%s, falling back to PathPrefix", ruleType, i.ObjectMeta.Namespace, i.ObjectMeta.Name)
|
||||
fallthrough
|
||||
case ruleType == "":
|
||||
ruleType := i.Annotations[annotationFrontendRuleType]
|
||||
if ruleType == "" {
|
||||
ruleType = ruleTypePathPrefix
|
||||
}
|
||||
|
||||
|
@ -265,17 +258,15 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
|||
}
|
||||
|
||||
if !exists {
|
||||
log.Errorf("Endpoints not found for %s/%s", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||
continue
|
||||
log.Warnf("Endpoints not found for %s/%s", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||
break
|
||||
}
|
||||
|
||||
if len(endpoints.Subsets) == 0 {
|
||||
log.Warnf("Service endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
|
||||
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(int(port.Port)),
|
||||
Weight: 1,
|
||||
log.Warnf("Endpoints not available for %s/%s", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
|
||||
for _, subset := range endpoints.Subsets {
|
||||
for _, address := range subset.Addresses {
|
||||
url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports))
|
||||
|
@ -290,7 +281,6 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -398,24 +388,3 @@ func (p *Provider) loadConfig(templateObjects types.Configuration) *types.Config
|
|||
}
|
||||
return configuration
|
||||
}
|
||||
|
||||
func getRuleTypeFromAnnotation(annotations map[string]string) (ruleType string, unknown bool) {
|
||||
ruleType = annotations[annotationFrontendRuleType]
|
||||
for _, knownRuleType := range []string{
|
||||
ruleTypePathPrefixStrip,
|
||||
ruleTypePathStrip,
|
||||
ruleTypePath,
|
||||
ruleTypePathPrefix,
|
||||
} {
|
||||
if strings.ToLower(ruleType) == strings.ToLower(knownRuleType) {
|
||||
return knownRuleType, false
|
||||
}
|
||||
}
|
||||
|
||||
if ruleType != "" {
|
||||
// Annotation is set but does not match anything we know.
|
||||
unknown = true
|
||||
}
|
||||
|
||||
return ruleType, unknown
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/types"
|
||||
|
@ -333,19 +332,24 @@ func TestRuleType(t *testing.T) {
|
|||
frontendRuleType string
|
||||
}{
|
||||
{
|
||||
desc: "implicit default",
|
||||
desc: "rule type annotation missing",
|
||||
ingressRuleType: "",
|
||||
frontendRuleType: ruleTypePathPrefix,
|
||||
},
|
||||
{
|
||||
desc: "unknown ingress / explicit default",
|
||||
ingressRuleType: "unknown",
|
||||
frontendRuleType: ruleTypePathPrefix,
|
||||
desc: "Path rule type annotation set",
|
||||
ingressRuleType: "Path",
|
||||
frontendRuleType: "Path",
|
||||
},
|
||||
{
|
||||
desc: "explicit ingress",
|
||||
ingressRuleType: ruleTypePath,
|
||||
frontendRuleType: ruleTypePath,
|
||||
desc: "PathStrip rule type annotation set",
|
||||
ingressRuleType: "PathStrip",
|
||||
frontendRuleType: "PathStrip",
|
||||
},
|
||||
{
|
||||
desc: "PathStripPrefix rule type annotation set",
|
||||
ingressRuleType: "PathStripPrefix",
|
||||
frontendRuleType: "PathStripPrefix",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1832,65 +1836,6 @@ func TestInvalidPassHostHeaderValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetRuleTypeFromAnnotation(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
wantedUnknown bool
|
||||
}{
|
||||
{
|
||||
in: ruleTypePathPrefixStrip,
|
||||
wantedUnknown: false,
|
||||
},
|
||||
{
|
||||
in: ruleTypePathStrip,
|
||||
wantedUnknown: false,
|
||||
},
|
||||
{
|
||||
in: ruleTypePath,
|
||||
wantedUnknown: false,
|
||||
},
|
||||
{
|
||||
in: ruleTypePathPrefix,
|
||||
wantedUnknown: false,
|
||||
},
|
||||
{
|
||||
wantedUnknown: false,
|
||||
},
|
||||
{
|
||||
in: "Unknown",
|
||||
wantedUnknown: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
inputs := []string{test.in, strings.ToLower(test.in)}
|
||||
if inputs[0] == inputs[1] {
|
||||
// Lower-casing makes no difference -- truncating to single case.
|
||||
inputs = inputs[:1]
|
||||
}
|
||||
for _, input := range inputs {
|
||||
t.Run(fmt.Sprintf("in='%s'", input), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
annotations := map[string]string{}
|
||||
if test.in != "" {
|
||||
annotations[annotationFrontendRuleType] = test.in
|
||||
}
|
||||
|
||||
gotRuleType, gotUnknown := getRuleTypeFromAnnotation(annotations)
|
||||
|
||||
if gotUnknown != test.wantedUnknown {
|
||||
t.Errorf("got unknown '%t', wanted '%t'", gotUnknown, test.wantedUnknown)
|
||||
}
|
||||
|
||||
if gotRuleType != test.in {
|
||||
t.Errorf("got rule type '%s', wanted '%s'", gotRuleType, test.in)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubeAPIErrors(t *testing.T) {
|
||||
ingresses := []*v1beta1.Ingress{{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
|
@ -2027,6 +1972,21 @@ func TestMissingResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Host: "missing_endpoint_subsets",
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "missing_endpoint_subsets_service",
|
||||
ServicePort: intstr.FromInt(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
@ -2061,6 +2021,21 @@ func TestMissingResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "missing_endpoint_subsets_service",
|
||||
UID: "4",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.4",
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
endpoints := []*v1.Endpoints{
|
||||
{
|
||||
|
@ -2084,6 +2059,14 @@ func TestMissingResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "missing_endpoint_subsets_service",
|
||||
UID: "4",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Subsets: []v1.EndpointSubset{},
|
||||
},
|
||||
}
|
||||
|
||||
watchChan := make(chan interface{})
|
||||
|
@ -2130,6 +2113,14 @@ func TestMissingResources(t *testing.T) {
|
|||
Sticky: false,
|
||||
},
|
||||
},
|
||||
"missing_endpoint_subsets": {
|
||||
Servers: map[string]types.Server{},
|
||||
CircuitBreaker: nil,
|
||||
LoadBalancer: &types.LoadBalancer{
|
||||
Method: "wrr",
|
||||
Sticky: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
Frontends: map[string]*types.Frontend{
|
||||
"fully_working": {
|
||||
|
@ -2150,6 +2141,15 @@ func TestMissingResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"missing_endpoint_subsets": {
|
||||
Backend: "missing_endpoint_subsets",
|
||||
PassHostHeader: true,
|
||||
Routes: map[string]types.Route{
|
||||
"missing_endpoint_subsets": {
|
||||
Rule: "Host:missing_endpoint_subsets",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -388,9 +388,13 @@ func parseRancherData(environments []*rancher.Environment, services []*rancher.S
|
|||
Containers: []string{},
|
||||
}
|
||||
|
||||
if service.LaunchConfig == nil || service.LaunchConfig.Labels == nil {
|
||||
log.Warnf("Rancher Service Labels are missing. Environment: %s, service: %s", environment.Name, service.Name)
|
||||
} else {
|
||||
for key, value := range service.LaunchConfig.Labels {
|
||||
rancherData.Labels[key] = value.(string)
|
||||
}
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
if container.Labels["io.rancher.stack_service.name"] == rancherData.Name && containerFilter(container) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[backends]{{range $backendName, $backend := .Backends}}
|
||||
[backends."{{$backendName}}"]
|
||||
{{if $backend.CircuitBreaker}}
|
||||
[backends."{{$backendName}}".circuitbreaker]
|
||||
expression = "{{$backend.CircuitBreaker.Expression}}"
|
||||
|
|
64
vendor/github.com/gambol99/go-marathon/application.go
generated
vendored
64
vendor/github.com/gambol99/go-marathon/application.go
generated
vendored
|
@ -89,6 +89,8 @@ type Application struct {
|
|||
TaskStats map[string]TaskStats `json:"taskStats,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
UpgradeStrategy *UpgradeStrategy `json:"upgradeStrategy,omitempty"`
|
||||
UnreachableStrategy *UnreachableStrategy `json:"unreachableStrategy,omitempty"`
|
||||
KillSelection string `json:"killSelection,omitempty"`
|
||||
Uris *[]string `json:"uris,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
VersionInfo *VersionInfo `json:"versionInfo,omitempty"`
|
||||
|
@ -453,7 +455,7 @@ func (r *Application) DeploymentIDs() []*DeploymentID {
|
|||
// CheckHTTP adds a HTTP check to an application
|
||||
// port: the port the check should be checking
|
||||
// interval: the interval in seconds the check should be performed
|
||||
func (r *Application) CheckHTTP(uri string, port, interval int) (*Application, error) {
|
||||
func (r *Application) CheckHTTP(path string, port, interval int) (*Application, error) {
|
||||
if r.Container == nil || r.Container.Docker == nil {
|
||||
return nil, ErrNoApplicationContainer
|
||||
}
|
||||
|
@ -464,7 +466,7 @@ func (r *Application) CheckHTTP(uri string, port, interval int) (*Application, e
|
|||
}
|
||||
health := NewDefaultHealthCheck()
|
||||
health.IntervalSeconds = interval
|
||||
*health.Path = uri
|
||||
*health.Path = path
|
||||
*health.PortIndex = portIndex
|
||||
// step: add to the checks
|
||||
r.AddHealthCheck(*health)
|
||||
|
@ -555,6 +557,20 @@ func (r *Application) EmptyUpgradeStrategy() *Application {
|
|||
return r
|
||||
}
|
||||
|
||||
// SetUnreachableStrategy sets the unreachable strategy.
|
||||
func (r *Application) SetUnreachableStrategy(us UnreachableStrategy) *Application {
|
||||
r.UnreachableStrategy = &us
|
||||
return r
|
||||
}
|
||||
|
||||
// EmptyUnreachableStrategy explicitly empties the unreachable strategy -- use this if
|
||||
// you need to empty the unreachable strategy of an application that already has
|
||||
// the unreachable strategy set (setting it to nil will keep the current value).
|
||||
func (r *Application) EmptyUnreachableStrategy() *Application {
|
||||
r.UnreachableStrategy = &UnreachableStrategy{}
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns the json representation of this application
|
||||
func (r *Application) String() string {
|
||||
s, err := json.MarshalIndent(r, "", " ")
|
||||
|
@ -611,9 +627,9 @@ func (r *marathonClient) HasApplicationVersion(name, version string) (bool, erro
|
|||
// ApplicationVersions is a list of versions which has been deployed with marathon for a specific application
|
||||
// name: the id used to identify the application
|
||||
func (r *marathonClient) ApplicationVersions(name string) (*ApplicationVersions, error) {
|
||||
uri := fmt.Sprintf("%s/versions", buildURI(name))
|
||||
path := fmt.Sprintf("%s/versions", buildPath(name))
|
||||
versions := new(ApplicationVersions)
|
||||
if err := r.apiGet(uri, nil, versions); err != nil {
|
||||
if err := r.apiGet(path, nil, versions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return versions, nil
|
||||
|
@ -623,9 +639,9 @@ 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) {
|
||||
uri := fmt.Sprintf(buildURI(name))
|
||||
path := fmt.Sprintf(buildPath(name))
|
||||
deploymentID := new(DeploymentID)
|
||||
if err := r.apiPut(uri, version, deploymentID); err != nil {
|
||||
if err := r.apiPut(path, version, deploymentID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -639,7 +655,7 @@ func (r *marathonClient) Application(name string) (*Application, error) {
|
|||
Application *Application `json:"app"`
|
||||
}
|
||||
|
||||
if err := r.apiGet(buildURI(name), nil, &wrapper); err != nil {
|
||||
if err := r.apiGet(buildPath(name), nil, &wrapper); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -650,7 +666,7 @@ func (r *marathonClient) Application(name string) (*Application, error) {
|
|||
// name: the id used to identify the application
|
||||
// opts: GetAppOpts request payload
|
||||
func (r *marathonClient) ApplicationBy(name string, opts *GetAppOpts) (*Application, error) {
|
||||
u, err := addOptions(buildURI(name), opts)
|
||||
path, err := addOptions(buildPath(name), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -658,7 +674,7 @@ func (r *marathonClient) ApplicationBy(name string, opts *GetAppOpts) (*Applicat
|
|||
Application *Application `json:"app"`
|
||||
}
|
||||
|
||||
if err := r.apiGet(u, nil, &wrapper); err != nil {
|
||||
if err := r.apiGet(path, nil, &wrapper); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -671,8 +687,8 @@ func (r *marathonClient) ApplicationBy(name string, opts *GetAppOpts) (*Applicat
|
|||
func (r *marathonClient) ApplicationByVersion(name, version string) (*Application, error) {
|
||||
app := new(Application)
|
||||
|
||||
uri := fmt.Sprintf("%s/versions/%s", buildURI(name), version)
|
||||
if err := r.apiGet(uri, nil, app); err != nil {
|
||||
path := fmt.Sprintf("%s/versions/%s", buildPath(name), version)
|
||||
if err := r.apiGet(path, nil, app); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -779,10 +795,10 @@ func (r *marathonClient) appExistAndRunning(name string) bool {
|
|||
// name: the id used to identify the application
|
||||
// force: used to force the delete operation in case of blocked deployment
|
||||
func (r *marathonClient) DeleteApplication(name string, force bool) (*DeploymentID, error) {
|
||||
uri := buildURIWithForceParam(name, force)
|
||||
path := buildPathWithForceParam(name, force)
|
||||
// step: check of the application already exists
|
||||
deployID := new(DeploymentID)
|
||||
if err := r.apiDelete(uri, nil, deployID); err != nil {
|
||||
if err := r.apiDelete(path, nil, deployID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -794,8 +810,8 @@ func (r *marathonClient) DeleteApplication(name string, force bool) (*Deployment
|
|||
func (r *marathonClient) RestartApplication(name string, force bool) (*DeploymentID, error) {
|
||||
deployment := new(DeploymentID)
|
||||
var options struct{}
|
||||
uri := buildURIWithForceParam(fmt.Sprintf("%s/restart", name), force)
|
||||
if err := r.apiPost(uri, &options, deployment); err != nil {
|
||||
path := buildPathWithForceParam(fmt.Sprintf("%s/restart", name), force)
|
||||
if err := r.apiPost(path, &options, deployment); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -810,9 +826,9 @@ func (r *marathonClient) ScaleApplicationInstances(name string, instances int, f
|
|||
changes := new(Application)
|
||||
changes.ID = validateID(name)
|
||||
changes.Instances = &instances
|
||||
uri := buildURIWithForceParam(name, force)
|
||||
path := buildPathWithForceParam(name, force)
|
||||
deployID := new(DeploymentID)
|
||||
if err := r.apiPut(uri, changes, deployID); err != nil {
|
||||
if err := r.apiPut(path, changes, deployID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -823,22 +839,22 @@ func (r *marathonClient) ScaleApplicationInstances(name string, instances int, f
|
|||
// application: the structure holding the application configuration
|
||||
func (r *marathonClient) UpdateApplication(application *Application, force bool) (*DeploymentID, error) {
|
||||
result := new(DeploymentID)
|
||||
uri := buildURIWithForceParam(application.ID, force)
|
||||
if err := r.apiPut(uri, application, result); err != nil {
|
||||
path := buildPathWithForceParam(application.ID, force)
|
||||
if err := r.apiPut(path, application, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func buildURIWithForceParam(path string, force bool) string {
|
||||
uri := buildURI(path)
|
||||
func buildPathWithForceParam(rootPath string, force bool) string {
|
||||
path := buildPath(rootPath)
|
||||
if force {
|
||||
uri += "?force=true"
|
||||
path += "?force=true"
|
||||
}
|
||||
return uri
|
||||
return path
|
||||
}
|
||||
|
||||
func buildURI(path string) string {
|
||||
func buildPath(path string) string {
|
||||
return fmt.Sprintf("%s/%s", marathonAPIApps, trimRootPath(path))
|
||||
}
|
||||
|
||||
|
|
35
vendor/github.com/gambol99/go-marathon/client.go
generated
vendored
35
vendor/github.com/gambol99/go-marathon/client.go
generated
vendored
|
@ -27,6 +27,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -238,23 +239,23 @@ func (r *marathonClient) Ping() (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (r *marathonClient) apiGet(uri string, post, result interface{}) error {
|
||||
return r.apiCall("GET", uri, post, result)
|
||||
func (r *marathonClient) apiGet(path string, post, result interface{}) error {
|
||||
return r.apiCall("GET", path, post, result)
|
||||
}
|
||||
|
||||
func (r *marathonClient) apiPut(uri string, post, result interface{}) error {
|
||||
return r.apiCall("PUT", uri, post, result)
|
||||
func (r *marathonClient) apiPut(path string, post, result interface{}) error {
|
||||
return r.apiCall("PUT", path, post, result)
|
||||
}
|
||||
|
||||
func (r *marathonClient) apiPost(uri string, post, result interface{}) error {
|
||||
return r.apiCall("POST", uri, post, result)
|
||||
func (r *marathonClient) apiPost(path string, post, result interface{}) error {
|
||||
return r.apiCall("POST", path, post, result)
|
||||
}
|
||||
|
||||
func (r *marathonClient) apiDelete(uri string, post, result interface{}) error {
|
||||
return r.apiCall("DELETE", uri, post, result)
|
||||
func (r *marathonClient) apiDelete(path string, post, result interface{}) error {
|
||||
return r.apiCall("DELETE", path, post, result)
|
||||
}
|
||||
|
||||
func (r *marathonClient) apiCall(method, url string, body, result interface{}) error {
|
||||
func (r *marathonClient) apiCall(method, path string, body, result interface{}) error {
|
||||
for {
|
||||
// step: marshall the request to json
|
||||
var requestBody []byte
|
||||
|
@ -266,7 +267,7 @@ func (r *marathonClient) apiCall(method, url string, body, result interface{}) e
|
|||
}
|
||||
|
||||
// step: create the API request
|
||||
request, member, err := r.buildAPIRequest(method, url, bytes.NewReader(requestBody))
|
||||
request, member, err := r.buildAPIRequest(method, path, bytes.NewReader(requestBody))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -317,7 +318,7 @@ func (r *marathonClient) apiCall(method, url string, body, result interface{}) e
|
|||
}
|
||||
|
||||
// buildAPIRequest creates a default API request
|
||||
func (r *marathonClient) buildAPIRequest(method, uri string, reader io.Reader) (request *http.Request, member string, err error) {
|
||||
func (r *marathonClient) buildAPIRequest(method, path string, reader io.Reader) (request *http.Request, member string, err error) {
|
||||
// Grab a member from the cluster
|
||||
member, err = r.hosts.getMember()
|
||||
if err != nil {
|
||||
|
@ -325,16 +326,22 @@ func (r *marathonClient) buildAPIRequest(method, uri string, reader io.Reader) (
|
|||
}
|
||||
|
||||
// Build the HTTP request to Marathon
|
||||
request, err = r.client.buildMarathonRequest(method, member, uri, reader)
|
||||
request, err = r.client.buildMarathonRequest(method, member, path, reader)
|
||||
if err != nil {
|
||||
return nil, member, err
|
||||
}
|
||||
return request, member, nil
|
||||
}
|
||||
|
||||
func (rc *httpClient) buildMarathonRequest(method string, member string, uri string, reader io.Reader) (request *http.Request, err error) {
|
||||
// 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) {
|
||||
if strings.HasPrefix(path, "/") {
|
||||
panic(fmt.Sprintf("Path '%s' must not start with a leading slash", path))
|
||||
}
|
||||
|
||||
// Create the endpoint URL
|
||||
url := fmt.Sprintf("%s/%s", member, uri)
|
||||
url := fmt.Sprintf("%s/%s", member, path)
|
||||
|
||||
// Instantiate an HTTP request
|
||||
request, err = http.NewRequest(method, url, reader)
|
||||
|
|
2
vendor/github.com/gambol99/go-marathon/cluster.go
generated
vendored
2
vendor/github.com/gambol99/go-marathon/cluster.go
generated
vendored
|
@ -131,7 +131,7 @@ func (c *cluster) markDown(endpoint string) {
|
|||
func (c *cluster) healthCheckNode(node *member) {
|
||||
// step: wait for the node to become active ... we are assuming a /ping is enough here
|
||||
for {
|
||||
req, err := c.client.buildMarathonRequest("GET", node.endpoint, "/ping", nil)
|
||||
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 {
|
||||
|
|
28
vendor/github.com/gambol99/go-marathon/group.go
generated
vendored
28
vendor/github.com/gambol99/go-marathon/group.go
generated
vendored
|
@ -106,12 +106,12 @@ func (r *marathonClient) Group(name string) (*Group, error) {
|
|||
// GroupsBy retrieves a list of all the groups from marathon by embed options
|
||||
// opts: GetGroupOpts request payload
|
||||
func (r *marathonClient) GroupsBy(opts *GetGroupOpts) (*Groups, error) {
|
||||
u, err := addOptions(marathonAPIGroups, opts)
|
||||
path, err := addOptions(marathonAPIGroups, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups := new(Groups)
|
||||
if err := r.apiGet(u, "", groups); err != nil {
|
||||
if err := r.apiGet(path, "", groups); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
|
@ -121,12 +121,12 @@ func (r *marathonClient) GroupsBy(opts *GetGroupOpts) (*Groups, error) {
|
|||
// name: the identifier for the group
|
||||
// opts: GetGroupOpts request payload
|
||||
func (r *marathonClient) GroupBy(name string, opts *GetGroupOpts) (*Group, error) {
|
||||
u, err := addOptions(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), opts)
|
||||
path, err := addOptions(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group := new(Group)
|
||||
if err := r.apiGet(u, nil, group); err != nil {
|
||||
if err := r.apiGet(path, nil, group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return group, nil
|
||||
|
@ -135,8 +135,8 @@ func (r *marathonClient) GroupBy(name string, opts *GetGroupOpts) (*Group, error
|
|||
// HasGroup checks if the group exists in marathon
|
||||
// name: the identifier for the group
|
||||
func (r *marathonClient) HasGroup(name string) (bool, error) {
|
||||
uri := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
err := r.apiCall("GET", uri, "", nil)
|
||||
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
err := r.apiCall("GET", path, "", nil)
|
||||
if err != nil {
|
||||
if apiErr, ok := err.(*APIError); ok && apiErr.ErrCode == ErrCodeNotFound {
|
||||
return false, nil
|
||||
|
@ -208,11 +208,9 @@ func (r *marathonClient) WaitOnGroup(name string, timeout time.Duration) error {
|
|||
// force: used to force the delete operation in case of blocked deployment
|
||||
func (r *marathonClient) DeleteGroup(name string, force bool) (*DeploymentID, error) {
|
||||
version := new(DeploymentID)
|
||||
uri := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
if force {
|
||||
uri = uri + "?force=true"
|
||||
}
|
||||
if err := r.apiDelete(uri, nil, version); err != nil {
|
||||
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
path = buildPathWithForceParam(path, force)
|
||||
if err := r.apiDelete(path, nil, version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -225,11 +223,9 @@ func (r *marathonClient) DeleteGroup(name string, force bool) (*DeploymentID, er
|
|||
// force: used to force the update operation in case of blocked deployment
|
||||
func (r *marathonClient) UpdateGroup(name string, group *Group, force bool) (*DeploymentID, error) {
|
||||
deploymentID := new(DeploymentID)
|
||||
uri := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
if force {
|
||||
uri = uri + "?force=true"
|
||||
}
|
||||
if err := r.apiPut(uri, group, deploymentID); err != nil {
|
||||
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
||||
path = buildPathWithForceParam(path, force)
|
||||
if err := r.apiPut(path, group, deploymentID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
4
vendor/github.com/gambol99/go-marathon/queue.go
generated
vendored
4
vendor/github.com/gambol99/go-marathon/queue.go
generated
vendored
|
@ -51,8 +51,8 @@ func (r *marathonClient) Queue() (*Queue, error) {
|
|||
// DeleteQueueDelay resets task launch delay of the specific application
|
||||
// appID: the ID of the application
|
||||
func (r *marathonClient) DeleteQueueDelay(appID string) error {
|
||||
uri := fmt.Sprintf("%s/%s/delay", marathonAPIQueue, trimRootPath(appID))
|
||||
err := r.apiDelete(uri, nil, nil)
|
||||
path := fmt.Sprintf("%s/%s/delay", marathonAPIQueue, trimRootPath(appID))
|
||||
err := r.apiDelete(path, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
4
vendor/github.com/gambol99/go-marathon/subscription.go
generated
vendored
4
vendor/github.com/gambol99/go-marathon/subscription.go
generated
vendored
|
@ -201,8 +201,8 @@ func (r *marathonClient) registerSSESubscription() error {
|
|||
// Subscribe adds a URL to Marathon's callback facility
|
||||
// callback : the URL you wish to subscribe
|
||||
func (r *marathonClient) Subscribe(callback string) error {
|
||||
uri := fmt.Sprintf("%s?callbackUrl=%s", marathonAPISubscription, callback)
|
||||
return r.apiPost(uri, "", nil)
|
||||
path := fmt.Sprintf("%s?callbackUrl=%s", marathonAPISubscription, callback)
|
||||
return r.apiPost(path, "", nil)
|
||||
|
||||
}
|
||||
|
||||
|
|
22
vendor/github.com/gambol99/go-marathon/task.go
generated
vendored
22
vendor/github.com/gambol99/go-marathon/task.go
generated
vendored
|
@ -79,13 +79,13 @@ func (r *Task) HasHealthCheckResults() bool {
|
|||
// AllTasks lists tasks of all applications.
|
||||
// opts: AllTasksOpts request payload
|
||||
func (r *marathonClient) AllTasks(opts *AllTasksOpts) (*Tasks, error) {
|
||||
u, err := addOptions(marathonAPITasks, opts)
|
||||
path, err := addOptions(marathonAPITasks, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tasks := new(Tasks)
|
||||
if err := r.apiGet(u, nil, tasks); err != nil {
|
||||
if err := r.apiGet(path, nil, tasks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -107,14 +107,14 @@ func (r *marathonClient) Tasks(id string) (*Tasks, error) {
|
|||
// id: the id of the application
|
||||
// opts: KillApplicationTasksOpts request payload
|
||||
func (r *marathonClient) KillApplicationTasks(id string, opts *KillApplicationTasksOpts) (*Tasks, error) {
|
||||
u := fmt.Sprintf("%s/%s/tasks", marathonAPIApps, trimRootPath(id))
|
||||
u, err := addOptions(u, opts)
|
||||
path := fmt.Sprintf("%s/%s/tasks", marathonAPIApps, trimRootPath(id))
|
||||
path, err := addOptions(path, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tasks := new(Tasks)
|
||||
if err := r.apiDelete(u, nil, tasks); err != nil {
|
||||
if err := r.apiDelete(path, nil, tasks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -129,8 +129,8 @@ func (r *marathonClient) KillTask(taskID string, opts *KillTaskOpts) (*Task, err
|
|||
appName = strings.Replace(appName, "_", "/", -1)
|
||||
taskID = strings.Replace(taskID, "/", "_", -1)
|
||||
|
||||
u := fmt.Sprintf("%s/%s/tasks/%s", marathonAPIApps, appName, taskID)
|
||||
u, err := addOptions(u, opts)
|
||||
path := fmt.Sprintf("%s/%s/tasks/%s", marathonAPIApps, appName, taskID)
|
||||
path, err := addOptions(path, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ func (r *marathonClient) KillTask(taskID string, opts *KillTaskOpts) (*Task, err
|
|||
Task Task `json:"task"`
|
||||
})
|
||||
|
||||
if err := r.apiDelete(u, nil, wrappedTask); err != nil {
|
||||
if err := r.apiDelete(path, nil, wrappedTask); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -150,8 +150,8 @@ func (r *marathonClient) KillTask(taskID string, opts *KillTaskOpts) (*Task, err
|
|||
// tasks: the array of task ids
|
||||
// opts: KillTaskOpts request payload
|
||||
func (r *marathonClient) KillTasks(tasks []string, opts *KillTaskOpts) error {
|
||||
u := fmt.Sprintf("%s/delete", marathonAPITasks)
|
||||
u, err := addOptions(u, opts)
|
||||
path := fmt.Sprintf("%s/delete", marathonAPITasks)
|
||||
path, err := addOptions(path, opts)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func (r *marathonClient) KillTasks(tasks []string, opts *KillTaskOpts) error {
|
|||
}
|
||||
post.IDs = tasks
|
||||
|
||||
return r.apiPost(u, &post, nil)
|
||||
return r.apiPost(path, &post, nil)
|
||||
}
|
||||
|
||||
// TaskEndpoints gets the endpoints i.e. HOST_IP:DYNAMIC_PORT for a specific application service
|
||||
|
|
35
vendor/github.com/gambol99/go-marathon/unreachable_strategy.go
generated
vendored
Normal file
35
vendor/github.com/gambol99/go-marathon/unreachable_strategy.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2017 Rohith 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
|
||||
|
||||
// UnreachableStrategy is the unreachable strategy applied to an application.
|
||||
type UnreachableStrategy struct {
|
||||
InactiveAfterSeconds *float64 `json:"inactiveAfterSeconds,omitempty"`
|
||||
ExpungeAfterSeconds *float64 `json:"expungeAfterSeconds,omitempty"`
|
||||
}
|
||||
|
||||
// SetInactiveAfterSeconds sets the period after which instance will be marked as inactive.
|
||||
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 {
|
||||
us.ExpungeAfterSeconds = &cap
|
||||
return us
|
||||
}
|
Loading…
Reference in a new issue