diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ec8f4fdc..6ec971d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## [v1.3.2](https://github.com/containous/traefik/tree/v1.3.2) (2017-06-29) +[All Commits](https://github.com/containous/traefik/compare/v1.3.1...v1.3.2) + +**Bug fixes:** +- **[acme]** Add provided certificate checking before LE certificate generation with OnHostRule option ([#1772](https://github.com/containous/traefik/pull/1772) by [nmengin](https://github.com/nmengin)) +- **[k8s]** Fix race on closing event channel. ([#1798](https://github.com/containous/traefik/pull/1798) by [timoreimann](https://github.com/timoreimann)) +- **[marathon]** Upgrade go-marathon to dd6cbd4. ([#1800](https://github.com/containous/traefik/pull/1800) by [timoreimann](https://github.com/timoreimann)) +- **[oxy,websocket]** Problem with keepalive when switching protocol failed ([#1782](https://github.com/containous/traefik/pull/1782) by [ldez](https://github.com/ldez)) +- **[oxy]** Fix proxying of unannounced trailers ([#1805](https://github.com/containous/traefik/pull/1805) by [ldez](https://github.com/ldez)) + ## [v1.3.1](https://github.com/containous/traefik/tree/v1.3.1) (2017-06-16) [All Commits](https://github.com/containous/traefik/compare/v1.3.0...v1.3.1) diff --git a/glide.lock b/glide.lock index 4b366e0e7..68f6cb42b 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 068db16642eb0249ce973711f4cf44206cc201214b1990e13f89e1ac3c402635 -updated: 2017-06-27T00:25:19.890844996+02:00 +hash: 4e2a6a4fe84b2843ff946fe56deae17f8c7666e2e6567d8a75f4b0bdd7cb0280 +updated: 2017-06-29T16:47:14.848940186+02:00 imports: - name: cloud.google.com/go version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c @@ -195,7 +195,7 @@ imports: - name: github.com/fatih/color version: 9131ab34cf20d2f6d83fdc67168a5430d1c7dc23 - name: github.com/gambol99/go-marathon - version: 1b9c2582c26b632fb1fb295776d7ce40b68c36f2 + version: dd6cbd4c2d71294a19fb89158f2a00d427f174ab - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee - name: github.com/go-ini/ini @@ -409,7 +409,7 @@ imports: - name: github.com/vdemeester/docker-events version: be74d4929ec1ad118df54349fda4b0cba60f849b - name: github.com/vulcand/oxy - version: ad5bdb606fa9c64db267f0e43d63834908bdb05e + version: 7da864c1d53bd58165435bb78bbf8c01f01c8f4a repo: https://github.com/containous/oxy.git vcs: git subpackages: diff --git a/glide.yaml b/glide.yaml index f56b535e7..c9cfd3277 100644 --- a/glide.yaml +++ b/glide.yaml @@ -9,7 +9,7 @@ import: - package: github.com/cenk/backoff - package: github.com/containous/flaeg - package: github.com/vulcand/oxy - version: ad5bdb606fa9c64db267f0e43d63834908bdb05e + version: 7da864c1d53bd58165435bb78bbf8c01f01c8f4a repo: https://github.com/containous/oxy.git vcs: git subpackages: @@ -96,7 +96,7 @@ import: - package: k8s.io/client-go version: v2.0.0 - package: github.com/gambol99/go-marathon - version: 1b9c2582c26b632fb1fb295776d7ce40b68c36f2 + version: dd6cbd4c2d71294a19fb89158f2a00d427f174ab - package: github.com/ArthurHlt/go-eureka-client subpackages: - eureka @@ -163,10 +163,10 @@ import: version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b - package: golang.org/x/oauth2 version: 7fdf09982454086d5570c7db3e11f360194830ca -- package: github.com/rancher/go-rancher-metadata - version: 95d4962a8f0420be24fb49c2cb4f5491284c62f1 subpackages: - google +- package: github.com/rancher/go-rancher-metadata + version: 95d4962a8f0420be24fb49c2cb4f5491284c62f1 - package: github.com/googleapis/gax-go version: 9af46dd5a1713e8b5cd71106287eba3cefdde50b - package: google.golang.org/grpc diff --git a/provider/kubernetes/client.go b/provider/kubernetes/client.go index 971efe033..4ee9097d2 100644 --- a/provider/kubernetes/client.go +++ b/provider/kubernetes/client.go @@ -263,7 +263,7 @@ func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan struct{}) (<-c // fireEvent checks if all controllers have synced before firing // Used after startup or a reconnect func (c *clientImpl) fireEvent(event interface{}, eventCh chan interface{}) { - if !c.ingController.HasSynced() || !c.svcController.HasSynced() || !c.epController.HasSynced() { + if !c.ingController.HasSynced() || !c.svcController.HasSynced() || !c.epController.HasSynced() || !c.secController.HasSynced() { return } eventHandlerFunc(eventCh, event) diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index 00fc9a826..0eacb1c3c 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -116,11 +116,11 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s } notify := func(err error, time time.Duration) { - log.Errorf("Provider connection error %+v, retrying in %s", err, time) + log.Errorf("Provider connection error: %s; retrying in %s", err, time) } err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify) if err != nil { - log.Errorf("Cannot connect to Provider server %+v", err) + log.Errorf("Cannot connect to Provider: %s", err) } }) @@ -180,7 +180,8 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists { basicAuthCreds, err := handleBasicAuthConfig(i, k8sClient) if err != nil { - return nil, err + log.Errorf("Failed to retrieve basic auth configuration for ingress %s/%s: %s", i.ObjectMeta.Namespace, i.ObjectMeta.Name, err) + continue } templateObjects.Frontends[r.Host+pa.Path] = &types.Frontend{ Backend: r.Host + pa.Path, @@ -300,18 +301,15 @@ func handleBasicAuthConfig(i *v1beta1.Ingress, k8sClient Client) ([]string, erro return nil, nil } if strings.ToLower(authType) != "basic" { - return nil, fmt.Errorf("unsupported auth-type: %q", authType) + return nil, fmt.Errorf("unsupported auth-type on annotation ingress.kubernetes.io/auth-type: %q", authType) } authSecret := i.Annotations[annotationKubernetesAuthSecret] if authSecret == "" { - return nil, errors.New("auth-secret annotation must be set") + return nil, errors.New("auth-secret annotation ingress.kubernetes.io/auth-secret must be set") } basicAuthCreds, err := loadAuthCredentials(i.Namespace, authSecret, k8sClient) if err != nil { - return nil, err - } - if len(basicAuthCreds) == 0 { - return nil, errors.New("secret file without credentials") + return nil, fmt.Errorf("failed to load auth credentials: %s", err) } return basicAuthCreds, nil } @@ -324,9 +322,9 @@ func loadAuthCredentials(namespace, secretName string, k8sClient Client) ([]stri case !ok: return nil, fmt.Errorf("secret %q/%q not found", namespace, secretName) case secret == nil: - return nil, errors.New("secret data must not be nil") + return nil, fmt.Errorf("data for secret %q/%q must not be nil", namespace, secretName) case len(secret.Data) != 1: - return nil, errors.New("secret must contain single element only") + return nil, fmt.Errorf("found %d elements for secret %q/%q, must be single element exactly", len(secret.Data), namespace, secretName) default: } var firstSecret []byte @@ -341,6 +339,10 @@ func loadAuthCredentials(namespace, secretName string, k8sClient Client) ([]stri creds = append(creds, cred) } } + if len(creds) == 0 { + return nil, fmt.Errorf("secret %q/%q does not contain any credentials", namespace, secretName) + } + return creds, nil } diff --git a/vendor/github.com/gambol99/go-marathon/client.go b/vendor/github.com/gambol99/go-marathon/client.go index 18a4efd3b..a042c560f 100644 --- a/vendor/github.com/gambol99/go-marathon/client.go +++ b/vendor/github.com/gambol99/go-marathon/client.go @@ -150,8 +150,6 @@ type Marathon interface { } var ( - // ErrInvalidResponse is thrown when marathon responds with invalid or error response - ErrInvalidResponse = errors.New("invalid response from Marathon") // ErrMarathonDown is thrown when all the marathon endpoints are down ErrMarathonDown = errors.New("all the Marathon hosts are presently down") // ErrTimeoutError is thrown when the operation has timed out @@ -303,8 +301,7 @@ func (r *marathonClient) apiCall(method, path string, body, result interface{}) if response.StatusCode >= 200 && response.StatusCode <= 299 { if result != nil { if err := json.Unmarshal(respBody, result); err != nil { - r.debugLog.Printf("apiCall(): failed to unmarshall the response from marathon, error: %s\n", err) - return ErrInvalidResponse + return fmt.Errorf("failed to unmarshal response from Marathon: %s", err) } } return nil diff --git a/vendor/github.com/gambol99/go-marathon/health.go b/vendor/github.com/gambol99/go-marathon/health.go index c264416b0..11c68e64d 100644 --- a/vendor/github.com/gambol99/go-marathon/health.go +++ b/vendor/github.com/gambol99/go-marathon/health.go @@ -27,7 +27,7 @@ type HealthCheck struct { GracePeriodSeconds int `json:"gracePeriodSeconds,omitempty"` IntervalSeconds int `json:"intervalSeconds,omitempty"` TimeoutSeconds int `json:"timeoutSeconds,omitempty"` - IgnoreHTTP1xx *bool `json:"ignoreHttp1xx,ommitempty"` + IgnoreHTTP1xx *bool `json:"ignoreHttp1xx,omitempty"` } // SetCommand sets the given command on the health check. diff --git a/vendor/github.com/gambol99/go-marathon/unreachable_strategy.go b/vendor/github.com/gambol99/go-marathon/unreachable_strategy.go index a15f7afe6..a77ff6936 100644 --- a/vendor/github.com/gambol99/go-marathon/unreachable_strategy.go +++ b/vendor/github.com/gambol99/go-marathon/unreachable_strategy.go @@ -16,12 +16,54 @@ limitations under the License. package marathon +import ( + "encoding/json" + "fmt" +) + +const UnreachableStrategyAbsenceReasonDisabled = "disabled" + // UnreachableStrategy is the unreachable strategy applied to an application. type UnreachableStrategy struct { + EnabledUnreachableStrategy + AbsenceReason string +} + +// EnabledUnreachableStrategy covers parameters pertaining to present unreachable strategies. +type EnabledUnreachableStrategy struct { InactiveAfterSeconds *float64 `json:"inactiveAfterSeconds,omitempty"` ExpungeAfterSeconds *float64 `json:"expungeAfterSeconds,omitempty"` } +type unreachableStrategy UnreachableStrategy + +// UnmarshalJSON unmarshals the given JSON into an UnreachableStrategy. It +// populates parameters for present strategies, and otherwise only sets the +// absence reason. +func (us *UnreachableStrategy) UnmarshalJSON(b []byte) error { + var u unreachableStrategy + var errEnabledUS, errNonEnabledUS error + if errEnabledUS = json.Unmarshal(b, &u); errEnabledUS == nil { + *us = UnreachableStrategy(u) + return nil + } + + if errNonEnabledUS = json.Unmarshal(b, &us.AbsenceReason); errNonEnabledUS == nil { + return nil + } + + return fmt.Errorf("failed to unmarshal unreachable strategy: unmarshaling into enabled returned error '%s'; unmarshaling into non-enabled returned error '%s'", errEnabledUS, errNonEnabledUS) +} + +// MarshalJSON marshals the unreachable strategy. +func (us *UnreachableStrategy) MarshalJSON() ([]byte, error) { + if us.AbsenceReason == "" { + return json.Marshal(us.EnabledUnreachableStrategy) + } + + return json.Marshal(us.AbsenceReason) +} + // SetInactiveAfterSeconds sets the period after which instance will be marked as inactive. func (us UnreachableStrategy) SetInactiveAfterSeconds(cap float64) UnreachableStrategy { us.InactiveAfterSeconds = &cap diff --git a/vendor/github.com/vulcand/oxy/forward/fwd.go b/vendor/github.com/vulcand/oxy/forward/fwd.go index 1e0cc74e6..28cb1e1c8 100644 --- a/vendor/github.com/vulcand/oxy/forward/fwd.go +++ b/vendor/github.com/vulcand/oxy/forward/fwd.go @@ -159,7 +159,9 @@ func (f *Forwarder) ServeHTTP(w http.ResponseWriter, req *http.Request) { // serveHTTP forwards HTTP traffic using the configured transport func (f *httpForwarder) serveHTTP(w http.ResponseWriter, req *http.Request, ctx *handlerContext) { start := time.Now().UTC() + response, err := f.roundTripper.RoundTrip(f.copyRequest(req, req.URL)) + if err != nil { ctx.log.Errorf("Error forwarding to %v, err: %v", req.URL, err) ctx.errHandler.ServeHTTP(w, req, err) @@ -169,6 +171,16 @@ func (f *httpForwarder) serveHTTP(w http.ResponseWriter, req *http.Request, ctx utils.CopyHeaders(w.Header(), response.Header) // Remove hop-by-hop headers. utils.RemoveHeaders(w.Header(), HopHeaders...) + + announcedTrailerKeyCount := len(response.Trailer) + if announcedTrailerKeyCount > 0 { + trailerKeys := make([]string, 0, announcedTrailerKeyCount) + for k := range response.Trailer { + trailerKeys = append(trailerKeys, k) + } + w.Header().Add("Trailer", strings.Join(trailerKeys, ", ")) + } + w.WriteHeader(response.StatusCode) stream := f.streamResponse @@ -179,6 +191,20 @@ func (f *httpForwarder) serveHTTP(w http.ResponseWriter, req *http.Request, ctx } } written, err := io.Copy(newResponseFlusher(w, stream), response.Body) + if err != nil { + ctx.log.Errorf("Error copying upstream response body: %v", err) + ctx.errHandler.ServeHTTP(w, req, err) + return + } + + defer response.Body.Close() + + forceSetTrailers := len(response.Trailer) != announcedTrailerKeyCount + shallowCopyTrailers(w.Header(), response.Trailer, forceSetTrailers) + + if written != 0 { + w.Header().Set(ContentLength, strconv.FormatInt(written, 10)) + } if req.TLS != nil { ctx.log.Infof("Round trip: %v, code: %v, duration: %v tls:version: %x, tls:resume:%t, tls:csuite:%x, tls:server:%v", @@ -192,17 +218,6 @@ func (f *httpForwarder) serveHTTP(w http.ResponseWriter, req *http.Request, ctx req.URL, response.StatusCode, time.Now().UTC().Sub(start)) } - defer response.Body.Close() - - if err != nil { - ctx.log.Errorf("Error copying upstream response Body: %v", err) - ctx.errHandler.ServeHTTP(w, req, err) - return - } - - if written != 0 { - w.Header().Set(ContentLength, strconv.FormatInt(written, 10)) - } } // copyRequest makes a copy of the specified request to be sent using the configured @@ -364,3 +379,12 @@ func isWebsocketRequest(req *http.Request) bool { } return containsHeader(Connection, "upgrade") && containsHeader(Upgrade, "websocket") } + +func shallowCopyTrailers(dstHeader, srcTrailer http.Header, forceSetTrailers bool) { + for k, vv := range srcTrailer { + if forceSetTrailers { + k = http.TrailerPrefix + k + } + dstHeader[k] = vv + } +}