diff --git a/.gitignore b/.gitignore index 5caf38ec1..9a57474fa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ gen.go .idea .intellij -log *.iml traefik traefik.toml diff --git a/acme/account.go b/acme/account.go index 55ac44672..52897db02 100644 --- a/acme/account.go +++ b/acme/account.go @@ -20,7 +20,14 @@ type Account struct { Registration *acme.RegistrationResource PrivateKey []byte DomainsCertificate DomainsCertificates - ChallengeCerts map[string][]byte + ChallengeCerts map[string]*ChallengeCert +} + +// ChallengeCert stores a challenge certificate +type ChallengeCert struct { + Certificate []byte + PrivateKey []byte + certificate *tls.Certificate } // Init inits acccount struct @@ -29,9 +36,27 @@ func (a *Account) Init() error { if err != nil { return err } + + for _, cert := range a.ChallengeCerts { + if cert.certificate == nil { + certificate, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey) + if err != nil { + return err + } + cert.certificate = &certificate + } + if cert.certificate.Leaf == nil { + leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0]) + if err != nil { + return err + } + cert.certificate.Leaf = leaf + } + } return nil } +// NewAccount creates an account func NewAccount(email string) (*Account, error) { // Create a user. New accounts need an email and private key to start privateKey, err := rsa.GenerateKey(rand.Reader, 4096) @@ -43,22 +68,22 @@ func NewAccount(email string) (*Account, error) { return &Account{ Email: email, PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey), - DomainsCertificate: domainsCerts, - ChallengeCerts: map[string][]byte{}}, nil + DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs}, + ChallengeCerts: map[string]*ChallengeCert{}}, nil } // GetEmail returns email -func (a Account) GetEmail() string { +func (a *Account) GetEmail() string { return a.Email } // GetRegistration returns lets encrypt registration resource -func (a Account) GetRegistration() *acme.RegistrationResource { +func (a *Account) GetRegistration() *acme.RegistrationResource { return a.Registration } // GetPrivateKey returns private key -func (a Account) GetPrivateKey() crypto.PrivateKey { +func (a *Account) GetPrivateKey() crypto.PrivateKey { if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil { return privateKey } @@ -81,6 +106,7 @@ type DomainsCertificates struct { lock sync.RWMutex } +// Init inits DomainsCertificates func (dc *DomainsCertificates) Init() error { dc.lock.Lock() defer dc.lock.Unlock() @@ -167,7 +193,7 @@ func (dc *DomainsCertificate) needRenew() bool { return true } // <= 7 days left, renew certificate - if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 7 * time.Hour))) { + if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 30 * time.Hour))) { return true } } diff --git a/acme/acme.go b/acme/acme.go index 575e7b2ac..045ff5572 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "errors" "fmt" + "github.com/cenk/backoff" "github.com/containous/staert" "github.com/containous/traefik/cluster" "github.com/containous/traefik/log" @@ -22,6 +23,7 @@ type ACME struct { Email string `description:"Email address used for registration"` Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"` Storage string `description:"File or key used for certificates storage."` + StorageFile string // deprecated OnDemand bool `description:"Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` OnHostRule bool `description:"Enable certificate generation on frontends Host rules."` CAServer string `description:"CA server to use."` @@ -82,6 +84,11 @@ func (a *ACME) init() error { return err } a.defaultCertificate = cert + // TODO: to remove in the futurs + if len(a.StorageFile) > 0 && len(a.Storage) == 0 { + log.Warnf("ACME.StorageFile is deprecated, use ACME.Storage instead") + a.Storage = a.StorageFile + } return nil } @@ -160,7 +167,6 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl if err != nil { return err } - log.Debugf("buildACMEClient...") a.client, err = a.buildACMEClient(account) if err != nil { return err @@ -179,7 +185,16 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl log.Debugf("AgreeToTOS...") err = a.client.AgreeToTOS() if err != nil { - return err + // Let's Encrypt Subscriber Agreement renew ? + reg, err := a.client.QueryRegistration() + if err != nil { + return err + } + account.Registration = reg + err = a.client.AgreeToTOS() + if err != nil { + log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error()) + } } err = transaction.Commit(account) if err != nil { @@ -302,7 +317,7 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat return challengeCert, nil } if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok { - log.Debugf("ACME got domaincert %s", clientHello.ServerName) + log.Debugf("ACME got domain cert %s", clientHello.ServerName) return domainCert.tlsCert, nil } if a.OnDemand { @@ -442,6 +457,22 @@ func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.C // LoadCertificateForDomains loads certificates from ACME for given domains func (a *ACME) LoadCertificateForDomains(domains []string) { safe.Go(func() { + operation := func() error { + if a.client == nil { + return fmt.Errorf("ACME client still not built") + } + return nil + } + notify := func(err error, time time.Duration) { + log.Errorf("Error getting ACME client: %v, retrying in %s", err, time) + } + ebo := backoff.NewExponentialBackOff() + ebo.MaxElapsedTime = 30 * time.Second + err := backoff.RetryNotify(operation, ebo, notify) + if err != nil { + log.Errorf("Error getting ACME client: %v", err) + return + } account := a.store.Get().(*Account) var domain Domain if len(domains) == 0 { diff --git a/acme/acme_test.go b/acme/acme_test.go index 399592022..ba7e7509d 100644 --- a/acme/acme_test.go +++ b/acme/acme_test.go @@ -63,7 +63,7 @@ func TestDomainsSetAppend(t *testing.T) { func TestCertificatesRenew(t *testing.T) { domainsCertificates := DomainsCertificates{ - lock: &sync.RWMutex{}, + lock: sync.RWMutex{}, Certs: []*DomainsCertificate{ { Domains: Domain{ diff --git a/acme/challengeProvider.go b/acme/challengeProvider.go index 1ed096b94..e66469258 100644 --- a/acme/challengeProvider.go +++ b/acme/challengeProvider.go @@ -2,23 +2,17 @@ package acme import ( "crypto/tls" + "strings" "sync" - "bytes" - "crypto/rsa" - "crypto/x509" - "encoding/gob" + "fmt" + "github.com/cenk/backoff" "github.com/containous/traefik/cluster" "github.com/containous/traefik/log" "github.com/xenolf/lego/acme" "time" ) -func init() { - gob.Register(rsa.PrivateKey{}) - gob.Register(rsa.PublicKey{}) -} - var _ acme.ChallengeProviderTimeout = (*challengeProvider)(nil) type challengeProvider struct { @@ -34,34 +28,44 @@ func newMemoryChallengeProvider(store cluster.Store) *challengeProvider { func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) { log.Debugf("Challenge GetCertificate %s", domain) + if !strings.HasSuffix(domain, ".acme.invalid") { + return nil, false + } c.lock.RLock() defer c.lock.RUnlock() account := c.store.Get().(*Account) if account.ChallengeCerts == nil { return nil, false } - if certBinary, ok := account.ChallengeCerts[domain]; ok { - cert := &tls.Certificate{} - var buffer bytes.Buffer - buffer.Write(certBinary) - dec := gob.NewDecoder(&buffer) - err := dec.Decode(cert) - if err != nil { - log.Errorf("Error unmarshaling challenge cert %s", err.Error()) - return nil, false + account.Init() + var result *tls.Certificate + operation := func() error { + for _, cert := range account.ChallengeCerts { + for _, dns := range cert.certificate.Leaf.DNSNames { + if domain == dns { + result = cert.certificate + return nil + } + } } - return cert, true + return fmt.Errorf("Cannot find challenge cert for domain %s", domain) } - return nil, false + notify := func(err error, time time.Duration) { + log.Errorf("Error getting cert: %v, retrying in %s", err, time) + } + ebo := backoff.NewExponentialBackOff() + ebo.MaxElapsedTime = 60 * time.Second + err := backoff.RetryNotify(operation, ebo, notify) + if err != nil { + log.Errorf("Error getting cert: %v", err) + return nil, false + } + return result, true } func (c *challengeProvider) Present(domain, token, keyAuth string) error { log.Debugf("Challenge Present %s", domain) - cert, _, err := acme.TLSSNI01ChallengeCert(keyAuth) - if err != nil { - return err - } - cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + cert, _, err := TLSSNI01ChallengeCert(keyAuth) if err != nil { return err } @@ -74,18 +78,9 @@ func (c *challengeProvider) Present(domain, token, keyAuth string) error { } account := object.(*Account) if account.ChallengeCerts == nil { - account.ChallengeCerts = map[string][]byte{} - } - for i := range cert.Leaf.DNSNames { - var buffer bytes.Buffer - enc := gob.NewEncoder(&buffer) - err := enc.Encode(cert) - if err != nil { - return err - } - account.ChallengeCerts[cert.Leaf.DNSNames[i]] = buffer.Bytes() - log.Debugf("Challenge Present cert: %s", cert.Leaf.DNSNames[i]) + account.ChallengeCerts = map[string]*ChallengeCert{} } + account.ChallengeCerts[domain] = &cert return transaction.Commit(account) } diff --git a/acme/crypto.go b/acme/crypto.go index 6fa544b70..c90f3d126 100644 --- a/acme/crypto.go +++ b/acme/crypto.go @@ -1,6 +1,8 @@ package acme import ( + "crypto" + "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha256" @@ -76,3 +78,48 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey) } + +// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge +func TLSSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) { + // generate a new RSA key for the certificates + var tempPrivKey crypto.PrivateKey + tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return ChallengeCert{}, "", err + } + rsaPrivKey := tempPrivKey.(*rsa.PrivateKey) + rsaPrivPEM := pemEncode(rsaPrivKey) + + zBytes := sha256.Sum256([]byte(keyAuth)) + z := hex.EncodeToString(zBytes[:sha256.Size]) + domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:]) + tempCertPEM, err := generatePemCert(rsaPrivKey, domain) + if err != nil { + return ChallengeCert{}, "", err + } + + certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM) + if err != nil { + return ChallengeCert{}, "", err + } + + return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil +} +func pemEncode(data interface{}) []byte { + var pemBlock *pem.Block + switch key := data.(type) { + case *ecdsa.PrivateKey: + keyBytes, _ := x509.MarshalECPrivateKey(key) + pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes} + case *rsa.PrivateKey: + pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)} + break + case *x509.CertificateRequest: + pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw} + break + case []byte: + pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))} + } + + return pem.EncodeToMemory(pemBlock) +} diff --git a/acme/localStore.go b/acme/localStore.go index c39322091..86d62867c 100644 --- a/acme/localStore.go +++ b/acme/localStore.go @@ -51,17 +51,6 @@ func (s *LocalStore) Load() (cluster.Object, error) { return account, nil } -// func (s *LocalStore) saveAccount(account *Account) error { -// s.storageLock.Lock() -// defer s.storageLock.Unlock() -// // write account to file -// data, err := json.MarshalIndent(account, "", " ") -// if err != nil { -// return err -// } -// return ioutil.WriteFile(s.file, data, 0644) -// } - // Begin creates a transaction with the KV store. func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) { s.storageLock.Lock() @@ -88,7 +77,7 @@ func (t *localTransaction) Commit(object cluster.Object) error { if err != nil { return err } - return ioutil.WriteFile(t.file, data, 0644) + err = ioutil.WriteFile(t.file, data, 0644) if err != nil { return err } diff --git a/cluster/datastore.go b/cluster/datastore.go index f2bcf9c61..a0c9599ad 100644 --- a/cluster/datastore.go +++ b/cluster/datastore.go @@ -3,10 +3,11 @@ package cluster import ( "encoding/json" "fmt" + "github.com/cenk/backoff" "github.com/containous/staert" + "github.com/containous/traefik/job" "github.com/containous/traefik/log" "github.com/docker/libkv/store" - "github.com/emilevauge/backoff" "github.com/satori/go.uuid" "golang.org/x/net/context" "sync" @@ -84,19 +85,11 @@ func (d *Datastore) watchChanges() error { cancel() return err } - d.localLock.Lock() - err := d.kv.LoadConfig(d.meta) + err = d.reload() if err != nil { - d.localLock.Unlock() return err } - err = d.meta.unmarshall() - if err != nil { - d.localLock.Unlock() - return err - } - d.localLock.Unlock() - // log.Debugf("Datastore object change received: %+v", d.object) + // log.Debugf("Datastore object change received: %+v", d.meta) if d.listener != nil { err := d.listener(d.meta.object) if err != nil { @@ -109,7 +102,7 @@ func (d *Datastore) watchChanges() error { notify := func(err error, time time.Duration) { log.Errorf("Error in watch datastore: %+v, retrying in %s", err, time) } - err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify) + err := backoff.RetryNotify(operation, job.NewBackOff(backoff.NewExponentialBackOff()), notify) if err != nil { log.Errorf("Error in watch datastore: %v", err) } @@ -117,6 +110,23 @@ func (d *Datastore) watchChanges() error { return nil } +func (d *Datastore) reload() error { + log.Debugf("Datastore reload") + d.localLock.Lock() + err := d.kv.LoadConfig(d.meta) + if err != nil { + d.localLock.Unlock() + return err + } + err = d.meta.unmarshall() + if err != nil { + d.localLock.Unlock() + return err + } + d.localLock.Unlock() + return nil +} + // Begin creates a transaction with the KV store. func (d *Datastore) Begin() (Transaction, Object, error) { id := uuid.NewV4().String() @@ -152,6 +162,10 @@ func (d *Datastore) Begin() (Transaction, Object, error) { } notify := func(err error, time time.Duration) { log.Errorf("Datastore sync error: %v, retrying in %s", err, time) + err = d.reload() + if err != nil { + log.Errorf("Error reloading: %+v", err) + } } ebo := backoff.NewExponentialBackOff() ebo.MaxElapsedTime = 60 * time.Second @@ -228,7 +242,6 @@ func (s *datastoreTransaction) Commit(object Object) error { } s.dirty = true - // log.Debugf("Datastore object saved: %+v", s.object) log.Debugf("Transaction commited %s", s.id) return nil } diff --git a/cluster/leadership.go b/cluster/leadership.go index 2143ee6d9..bcdff6a1d 100644 --- a/cluster/leadership.go +++ b/cluster/leadership.go @@ -1,11 +1,11 @@ package cluster import ( + "github.com/cenk/backoff" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/docker/leadership" - "github.com/emilevauge/backoff" "golang.org/x/net/context" "time" ) diff --git a/glide.lock b/glide.lock index 26c72cbbc..31570349c 100644 --- a/glide.lock +++ b/glide.lock @@ -1,3 +1,4 @@ +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 <<<<<<< 2fbcca003e6454c848801c859d8563da94ea8aaf <<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4 hash: c0ac205a859d78847e21d3cd63f427ffba985755c6ae84373e4a20364ba39b05 @@ -16,7 +17,14 @@ updated: 2016-09-20T14:50:04.029710103+02:00 hash: 49c7bd0e32b2764248183bda52f168fe22d69e2db5e17c1dbeebbe71be9929b1 updated: 2016-08-11T14:33:42.826534934+02:00 >>>>>>> Add ACME store +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 >>>>>>> Add ACME store +======= +======= +hash: af34f34bc4f9f3cc6c988caa4cc273a7f32f91b24c77bdf5cadd9bcb48883a53 +updated: 2016-09-28T11:40:41.311876377+02:00 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding imports: - name: github.com/abbot/go-http-auth version: cb4372376e1e00e9f6ab9ec142e029302c9e7140 @@ -41,7 +49,7 @@ imports: - name: github.com/containous/mux version: a819b77bba13f0c0cbe36e437bc2e948411b3996 - name: github.com/containous/staert - version: 56058c7d4152831a641764d10ec91132adf061ea + version: 92329254783dc01174f03302d51d7cf2c9ff84cf - name: github.com/coreos/etcd version: 1c9e0a0e33051fed6c05c141e6fcbfe5c7f2a899 subpackages: @@ -53,15 +61,25 @@ imports: subpackages: - spew - name: github.com/docker/distribution +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 <<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab version: 87917f30529e6a7fca8eaff2932424915fb11225 ======= +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 +>>>>>>> Challenge certs PEM encoding <<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4 version: 99cb7c0946d2f5a38015443e515dc916295064d7 ======= version: 857d0f15c0a4d8037175642e0ca3660829551cb5 >>>>>>> Add KV datastore +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 >>>>>>> Add KV datastore +======= +======= + version: 87917f30529e6a7fca8eaff2932424915fb11225 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - context - digest @@ -147,10 +165,14 @@ imports: - sockets - tlsconfig - name: github.com/docker/go-units +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 <<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab version: f2d77a61e3c169b43402a0a1e84f06daf29b8190 ======= version: f2145db703495b2e525c59662db69a7344b00bb8 +======= + version: f2d77a61e3c169b43402a0a1e84f06daf29b8190 +>>>>>>> Challenge certs PEM encoding - name: github.com/docker/leadership version: bfc7753dd48af19513b29deec23c364bf0f274eb >>>>>>> Add KV datastore @@ -172,11 +194,15 @@ imports: - version - yaml - name: github.com/docker/libkv +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 <<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab version: 35d3e2084c650109e7bcc7282655b1bc8ba924ff ======= version: aabc039ad04deb721e234f99cd1b4aa28ac71a40 >>>>>>> Add KV datastore +======= + version: 35d3e2084c650109e7bcc7282655b1bc8ba924ff +>>>>>>> Challenge certs PEM encoding subpackages: - store - store/boltdb @@ -192,15 +218,25 @@ imports: - name: github.com/go-check/check version: 4f90aeace3a26ad7021961c297b22c42160c7b25 - name: github.com/gogo/protobuf +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 <<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab version: e33835a643a970c11ac74f6333f5f6866387a101 ======= +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 +>>>>>>> Challenge certs PEM encoding <<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4 version: 89f1976ff373a3e549675d2f212c10f98b6c6316 ======= version: e57a569e1882958f6b188cb42231d6db87701f2a >>>>>>> Add KV datastore +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 >>>>>>> Add KV datastore +======= +======= + version: e33835a643a970c11ac74f6333f5f6866387a101 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - proto - name: github.com/golang/glog @@ -212,13 +248,29 @@ imports: - name: github.com/gorilla/context version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 - name: github.com/hashicorp/consul +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 + version: d5b7530ec593f1ec2a8f8a7c145bcadafa88b572 +======= + version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - api - name: github.com/hashicorp/go-cleanhttp version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d - name: github.com/hashicorp/serf +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 version: 6c4672d66fc6312ddde18399262943e21175d831 +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 + version: b7a120a5fc494f6dd5e858f42fd0fd4022d6320f +======= + version: 6c4672d66fc6312ddde18399262943e21175d831 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - coordinate - serf @@ -263,11 +315,15 @@ imports: - name: github.com/miekg/dns version: 5d001d020961ae1c184f9f8152fdc73810481677 - name: github.com/mitchellh/mapstructure +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 <<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab version: d2dd0262208475919e1a362f675cfc0e7c10e905 ======= version: 21a35fb16463dfb7c8eee579c65d995d95e64d1e >>>>>>> Add KV datastore +======= + version: d2dd0262208475919e1a362f675cfc0e7c10e905 +>>>>>>> Challenge certs PEM encoding - name: github.com/moul/http2curl version: b1479103caacaa39319f75e7f57fc545287fca0d - name: github.com/NYTimes/gziphandler @@ -275,15 +331,25 @@ imports: - name: github.com/ogier/pflag version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 <<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab version: 1a81e9ab1f138c091fe5c86d0883f87716088527 ======= +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 +>>>>>>> Challenge certs PEM encoding <<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4 version: d9fec4c63b089ddfc267194ecb6cda58a13f072c ======= version: ff88baa42fa5b2a1568a3a14665142fb4bdb3a2a >>>>>>> Add KV datastore +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 >>>>>>> Add KV datastore +======= +======= + version: 1a81e9ab1f138c091fe5c86d0883f87716088527 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest @@ -342,7 +408,15 @@ imports: - name: github.com/vulcand/route version: cb89d787ddbb1c5849a7ac9f79004c1fd12a4a32 - name: github.com/vulcand/vulcand +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 version: 28a4e5c0892167589737b95ceecbcef00295be50 +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 + version: 643ca8acff8386e3b276f6feb8ba9b5893dbc4a2 +======= + version: 28a4e5c0892167589737b95ceecbcef00295be50 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - conntracker - plugin @@ -372,11 +446,27 @@ imports: - name: gopkg.in/fsnotify.v1 version: a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb - name: gopkg.in/mgo.v2 +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 version: 29cc868a5ca65f401ff318143f9408d02f4799cc subpackages: - bson - name: gopkg.in/square/go-jose.v1 version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 + version: 22287bab4379e1fbf6002fb4eb769888f3fb224c + subpackages: + - bson +- name: gopkg.in/square/go-jose.v1 + version: aa2e30fdd1fe9dd3394119af66451ae790d50e0d +======= + version: 29cc868a5ca65f401ff318143f9408d02f4799cc + subpackages: + - bson +- name: gopkg.in/square/go-jose.v1 + version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding subpackages: - cipher - json @@ -394,7 +484,15 @@ testImports: - name: github.com/libkermit/docker-check version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3 - name: github.com/spf13/pflag +<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2 version: 5644820622454e71517561946e3d94b9f9db6842 +======= +<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829 + version: 08b1a584251b5b62f458943640fc8ebd4d50aaa5 +======= + version: 5644820622454e71517561946e3d94b9f9db6842 +>>>>>>> Challenge certs PEM encoding +>>>>>>> Challenge certs PEM encoding - name: github.com/vbatts/tar-split version: 6810cedb21b2c3d0b9bb8f9af12ff2dc7a2f14df subpackages: diff --git a/glide.yaml b/glide.yaml index 15b1b8be0..6230825c3 100644 --- a/glide.yaml +++ b/glide.yaml @@ -21,7 +21,7 @@ import: - stream - utils - package: github.com/containous/staert - version: 56058c7d4152831a641764d10ec91132adf061ea + version: 92329254783dc01174f03302d51d7cf2c9ff84cf - package: github.com/docker/engine-api version: 62043eb79d581a32ea849645277023c550732e52 subpackages: diff --git a/integration/consul_test.go b/integration/consul_test.go index ce60c38f9..d9b7b8218 100644 --- a/integration/consul_test.go +++ b/integration/consul_test.go @@ -446,12 +446,12 @@ func (s *ConsulSuite) TestDatastore(c *check.C) { c.Assert(err, checker.IsNil) ctx := context.Background() - datastore1, err := cluster.NewDataStore(kvSource, ctx, &TestStruct{}) + datastore1, err := cluster.NewDataStore(*kvSource, ctx, &TestStruct{}, nil) c.Assert(err, checker.IsNil) - datastore2, err := cluster.NewDataStore(kvSource, ctx, &TestStruct{}) + datastore2, err := cluster.NewDataStore(*kvSource, ctx, &TestStruct{}, nil) c.Assert(err, checker.IsNil) - setter1, err := datastore1.Begin() + setter1, _, err := datastore1.Begin() c.Assert(err, checker.IsNil) err = setter1.Commit(&TestStruct{ String: "foo", @@ -465,7 +465,7 @@ func (s *ConsulSuite) TestDatastore(c *check.C) { test2 := datastore2.Get().(*TestStruct) c.Assert(test2.String, checker.Equals, "foo") - setter2, err := datastore2.Begin() + setter2, _, err := datastore2.Begin() c.Assert(err, checker.IsNil) err = setter2.Commit(&TestStruct{ String: "bar", @@ -483,7 +483,7 @@ func (s *ConsulSuite) TestDatastore(c *check.C) { wg.Add(4) go func() { for i := 0; i < 100; i++ { - setter1, err := datastore1.Begin() + setter1, _, err := datastore1.Begin() c.Assert(err, checker.IsNil) err = setter1.Commit(&TestStruct{ String: "datastore1", @@ -495,7 +495,7 @@ func (s *ConsulSuite) TestDatastore(c *check.C) { }() go func() { for i := 0; i < 100; i++ { - setter2, err := datastore2.Begin() + setter2, _, err := datastore2.Begin() c.Assert(err, checker.IsNil) err = setter2.Commit(&TestStruct{ String: "datastore2", diff --git a/log/logger.go b/log/logger.go new file mode 100644 index 000000000..9ef60fc27 --- /dev/null +++ b/log/logger.go @@ -0,0 +1,188 @@ +package log + +import ( + "github.com/Sirupsen/logrus" + "io" +) + +var ( + logger *logrus.Entry +) + +func init() { + logger = logrus.StandardLogger().WithFields(logrus.Fields{}) +} + +// Context sets the Context of the logger +func Context(context interface{}) *logrus.Entry { + return logger.WithField("context", context) +} + +// SetOutput sets the standard logger output. +func SetOutput(out io.Writer) { + logrus.SetOutput(out) +} + +// SetFormatter sets the standard logger formatter. +func SetFormatter(formatter logrus.Formatter) { + logrus.SetFormatter(formatter) +} + +// SetLevel sets the standard logger level. +func SetLevel(level logrus.Level) { + logrus.SetLevel(level) +} + +// GetLevel returns the standard logger level. +func GetLevel() logrus.Level { + return logrus.GetLevel() +} + +// AddHook adds a hook to the standard logger hooks. +func AddHook(hook logrus.Hook) { + logrus.AddHook(hook) +} + +// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +func WithError(err error) *logrus.Entry { + return logger.WithError(err) +} + +// WithField creates an entry from the standard logger and adds a field to +// it. If you want multiple fields, use `WithFields`. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithField(key string, value interface{}) *logrus.Entry { + return logger.WithField(key, value) +} + +// WithFields creates an entry from the standard logger and adds multiple +// fields to it. This is simply a helper for `WithField`, invoking it +// once for each field. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithFields(fields logrus.Fields) *logrus.Entry { + return logger.WithFields(fields) +} + +// Debug logs a message at level Debug on the standard logger. +func Debug(args ...interface{}) { + logger.Debug(args...) +} + +// Print logs a message at level Info on the standard logger. +func Print(args ...interface{}) { + logger.Print(args...) +} + +// Info logs a message at level Info on the standard logger. +func Info(args ...interface{}) { + logger.Info(args...) +} + +// Warn logs a message at level Warn on the standard logger. +func Warn(args ...interface{}) { + logger.Warn(args...) +} + +// Warning logs a message at level Warn on the standard logger. +func Warning(args ...interface{}) { + logger.Warning(args...) +} + +// Error logs a message at level Error on the standard logger. +func Error(args ...interface{}) { + logger.Error(args...) +} + +// Panic logs a message at level Panic on the standard logger. +func Panic(args ...interface{}) { + logger.Panic(args...) +} + +// Fatal logs a message at level Fatal on the standard logger. +func Fatal(args ...interface{}) { + logger.Fatal(args...) +} + +// Debugf logs a message at level Debug on the standard logger. +func Debugf(format string, args ...interface{}) { + logger.Debugf(format, args...) +} + +// Printf logs a message at level Info on the standard logger. +func Printf(format string, args ...interface{}) { + logger.Printf(format, args...) +} + +// Infof logs a message at level Info on the standard logger. +func Infof(format string, args ...interface{}) { + logger.Infof(format, args...) +} + +// Warnf logs a message at level Warn on the standard logger. +func Warnf(format string, args ...interface{}) { + logger.Warnf(format, args...) +} + +// Warningf logs a message at level Warn on the standard logger. +func Warningf(format string, args ...interface{}) { + logger.Warningf(format, args...) +} + +// Errorf logs a message at level Error on the standard logger. +func Errorf(format string, args ...interface{}) { + logger.Errorf(format, args...) +} + +// Panicf logs a message at level Panic on the standard logger. +func Panicf(format string, args ...interface{}) { + logger.Panicf(format, args...) +} + +// Fatalf logs a message at level Fatal on the standard logger. +func Fatalf(format string, args ...interface{}) { + logger.Fatalf(format, args...) +} + +// Debugln logs a message at level Debug on the standard logger. +func Debugln(args ...interface{}) { + logger.Debugln(args...) +} + +// Println logs a message at level Info on the standard logger. +func Println(args ...interface{}) { + logger.Println(args...) +} + +// Infoln logs a message at level Info on the standard logger. +func Infoln(args ...interface{}) { + logger.Infoln(args...) +} + +// Warnln logs a message at level Warn on the standard logger. +func Warnln(args ...interface{}) { + logger.Warnln(args...) +} + +// Warningln logs a message at level Warn on the standard logger. +func Warningln(args ...interface{}) { + logger.Warningln(args...) +} + +// Errorln logs a message at level Error on the standard logger. +func Errorln(args ...interface{}) { + logger.Errorln(args...) +} + +// Panicln logs a message at level Panic on the standard logger. +func Panicln(args ...interface{}) { + logger.Panicln(args...) +} + +// Fatalln logs a message at level Fatal on the standard logger. +func Fatalln(args ...interface{}) { + logger.Fatalln(args...) +} diff --git a/middlewares/authenticator.go b/middlewares/authenticator.go index 33fb77ce9..380287e3f 100644 --- a/middlewares/authenticator.go +++ b/middlewares/authenticator.go @@ -2,9 +2,9 @@ package middlewares import ( "fmt" - "github.com/containous/traefik/log" "github.com/abbot/go-http-auth" "github.com/codegangsta/negroni" + "github.com/containous/traefik/log" "github.com/containous/traefik/types" "net/http" "strings" diff --git a/provider/consul_catalog.go b/provider/consul_catalog.go index 947368a68..aad2e8ae5 100644 --- a/provider/consul_catalog.go +++ b/provider/consul_catalog.go @@ -10,9 +10,9 @@ import ( "github.com/BurntSushi/ty/fun" "github.com/Sirupsen/logrus" - "github.com/containous/traefik/log" "github.com/cenk/backoff" "github.com/containous/traefik/job" + "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/hashicorp/consul/api" diff --git a/provider/docker.go b/provider/docker.go index 175cd3ebb..411f18696 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -13,9 +13,9 @@ import ( "golang.org/x/net/context" "github.com/BurntSushi/ty/fun" - "github.com/containous/traefik/log" "github.com/cenk/backoff" "github.com/containous/traefik/job" + "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/containous/traefik/version" diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 22b9fd18e..3ca6cd681 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -16,9 +16,6 @@ import ( "github.com/cenk/backoff" "github.com/containous/traefik/job" - "github.com/containous/traefik/provider/k8s" - "github.com/containous/traefik/safe" - "github.com/containous/traefik/types" ) const ( diff --git a/provider/kv.go b/provider/kv.go index 27b5469b2..db8bac553 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -9,9 +9,9 @@ import ( "errors" "github.com/BurntSushi/ty/fun" - "github.com/containous/traefik/log" "github.com/cenk/backoff" "github.com/containous/traefik/job" + "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/docker/libkv" @@ -148,7 +148,7 @@ func (provider *Kv) list(keys ...string) []string { joinedKeys := strings.Join(keys, "") keysPairs, err := provider.kvclient.List(joinedKeys) if err != nil { - log.Warnf("Cannot get keys %s %s ", joinedKeys, err) + log.Debugf("Cannot get keys %s %s ", joinedKeys, err) return nil } directoryKeys := make(map[string]string) @@ -170,10 +170,10 @@ func (provider *Kv) get(defaultValue string, keys ...string) string { joinedKeys := strings.Join(keys, "") keyPair, err := provider.kvclient.Get(strings.TrimPrefix(joinedKeys, "/")) if err != nil { - log.Warnf("Cannot get key %s %s, setting default %s", joinedKeys, err, defaultValue) + log.Debugf("Cannot get key %s %s, setting default %s", joinedKeys, err, defaultValue) return defaultValue } else if keyPair == nil { - log.Warnf("Cannot get key %s, setting default %s", joinedKeys, defaultValue) + log.Debugf("Cannot get key %s, setting default %s", joinedKeys, defaultValue) return defaultValue } return string(keyPair.Value) @@ -183,10 +183,10 @@ func (provider *Kv) splitGet(keys ...string) []string { joinedKeys := strings.Join(keys, "") keyPair, err := provider.kvclient.Get(joinedKeys) if err != nil { - log.Warnf("Cannot get key %s %s, setting default empty", joinedKeys, err) + log.Debugf("Cannot get key %s %s, setting default empty", joinedKeys, err) return []string{} } else if keyPair == nil { - log.Warnf("Cannot get key %s, setting default %empty", joinedKeys) + log.Debugf("Cannot get key %s, setting default %empty", joinedKeys) return []string{} } return strings.Split(string(keyPair.Value), ",") diff --git a/provider/marathon.go b/provider/marathon.go index ddeefe923..39934ec3e 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -13,9 +13,9 @@ import ( "time" "github.com/BurntSushi/ty/fun" - "github.com/containous/traefik/log" "github.com/cenk/backoff" "github.com/containous/traefik/job" + "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/gambol99/go-marathon" diff --git a/provider/mesos.go b/provider/mesos.go index 0943a3db6..5d8378412 100644 --- a/provider/mesos.go +++ b/provider/mesos.go @@ -8,9 +8,9 @@ import ( "fmt" "github.com/BurntSushi/ty/fun" - "github.com/containous/traefik/log" "github.com/cenk/backoff" "github.com/containous/traefik/job" + "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/mesos/mesos-go/detector" diff --git a/provider/provider.go b/provider/provider.go index 72610c7af..b2e7d5d09 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -13,8 +13,8 @@ import ( "os" "github.com/BurntSushi/toml" - "github.com/containous/traefik/log" "github.com/containous/traefik/autogen" + "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" ) diff --git a/rules.go b/rules.go index 6527c7c0e..c091e0fa6 100644 --- a/rules.go +++ b/rules.go @@ -149,7 +149,7 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f err := onRule(functionName, parsedFunction, parsedArgs) if err != nil { - return fmt.Errorf("Parsing error on rule:", err) + return fmt.Errorf("Parsing error on rule: %v", err) } } return nil @@ -180,7 +180,7 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) { return nil }) if err != nil { - return nil, fmt.Errorf("Error parsing rule:", err) + return nil, fmt.Errorf("Error parsing rule: %v", err) } return resultRoute, nil } @@ -195,7 +195,7 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) { return nil }) if err != nil { - return nil, fmt.Errorf("Error parsing domains:", err) + return nil, fmt.Errorf("Error parsing domains: %v", err) } return domains, nil } diff --git a/server.go b/server.go index 5ace528aa..4846e1885 100644 --- a/server.go +++ b/server.go @@ -243,7 +243,7 @@ func (server *Server) defaultConfigurationValues(configuration *types.Configurat for backendName, backend := range configuration.Backends { _, err := types.NewLoadBalancerMethod(backend.LoadBalancer) if err != nil { - log.Debugf("Error loading load balancer method '%+v' for backend %s: %v. Using default wrr.", backend.LoadBalancer, backendName, err) + log.Debugf("Load balancer method '%+v' for backend %s: %v. Using default wrr.", backend.LoadBalancer, backendName, err) backend.LoadBalancer = &types.LoadBalancer{Method: "wrr"} } } diff --git a/traefik.go b/traefik.go index 397133819..cfd5d11e6 100644 --- a/traefik.go +++ b/traefik.go @@ -129,7 +129,7 @@ Complete documentation is available at https://traefik.io`, } if _, err := f.Parse(usedCmd); err != nil { - fmtlog.Println(err) + fmtlog.Printf("Error parsing command: %s\n", err) os.Exit(-1) } @@ -150,7 +150,7 @@ Complete documentation is available at https://traefik.io`, kv, err = CreateKvSource(traefikConfiguration) if err != nil { - fmtlog.Println(err) + fmtlog.Printf("Error creating kv store: %s\n", err) os.Exit(-1) } @@ -164,13 +164,13 @@ Complete documentation is available at https://traefik.io`, } s.AddSource(kv) if _, err := s.LoadConfig(); err != nil { - fmtlog.Println(err) + fmtlog.Printf("Error loading configuration: %s\n", err) os.Exit(-1) } } if err := s.Run(); err != nil { - fmtlog.Println(err) + fmtlog.Printf("Error running traefik: %s\n", err) os.Exit(-1) } diff --git a/web.go b/web.go index 974c2fbf8..97b31ff17 100644 --- a/web.go +++ b/web.go @@ -8,10 +8,10 @@ import ( "net/http" "runtime" - "github.com/containous/traefik/log" "github.com/codegangsta/negroni" "github.com/containous/mux" "github.com/containous/traefik/autogen" + "github.com/containous/traefik/log" "github.com/containous/traefik/middlewares" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" @@ -79,7 +79,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag body, _ := ioutil.ReadAll(request.Body) err := json.Unmarshal(body, configuration) if err == nil { - configurationChan <- types.ConfigMessage{"web", configuration} + configurationChan <- types.ConfigMessage{ProviderName: "web", Configuration: configuration} provider.getConfigHandler(response, request) } else { log.Errorf("Error parsing configuration %+v", err)