From e948a013cdf866caa9d0b14366d74fd575a6fffe Mon Sep 17 00:00:00 2001 From: Ed Robinson Date: Fri, 20 May 2016 17:34:57 +0100 Subject: [PATCH 01/19] Build backend config using the K8S endpoint resource. * Potentialy saves a network hop * Ability to configure LB algothim (given some work to expose an anotation etc...) * K8s config Watch is triggered far less often --- provider/k8s/client.go | 55 +++++++++++------------- provider/k8s/endpoints.go | 84 +++++++++++++++++++++++++++++++++++++ provider/kubernetes.go | 38 +++++++++++++++-- provider/kubernetes_test.go | 75 +++++++++++++++++++++++++++++---- 4 files changed, 210 insertions(+), 42 deletions(-) create mode 100644 provider/k8s/endpoints.go diff --git a/provider/k8s/client.go b/provider/k8s/client.go index 957d66a9b..930c9835e 100644 --- a/provider/k8s/client.go +++ b/provider/k8s/client.go @@ -22,6 +22,7 @@ const ( type Client interface { GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) GetServices(predicate func(Service) bool) ([]Service, error) + GetEndpoints(name, namespace string) (Endpoints, error) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) } @@ -104,21 +105,26 @@ func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan e return c.watch(getURL, stopCh) } -// WatchEvents returns events in the cluster -func (c *clientImpl) WatchEvents(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/events" - return c.watch(getURL, stopCh) +// GetEndpoints returns the named Endpoints +// Endpoints have the same name as the coresponding service +func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) { + getURL := c.endpointURL + APIEndpoint + "/namespaces/" + namespace + "/endpoints/" + name + + body, err := c.do(c.request(getURL)) + if err != nil { + return Endpoints{}, fmt.Errorf("failed to create endpoints request: GET %q : %v", getURL, err) + } + + var endpoints Endpoints + if err := json.Unmarshal(body, &endpoints); err != nil { + return Endpoints{}, fmt.Errorf("failed to decode endpoints resources: %v", err) + } + return endpoints, nil } -// WatchPods returns pods in the cluster -func (c *clientImpl) WatchPods(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/pods" - return c.watch(getURL, stopCh) -} - -// WatchReplicationControllers returns ReplicationControllers in the cluster -func (c *clientImpl) WatchReplicationControllers(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/replicationcontrollers" +// WatchEndpoints returns endpoints in the cluster +func (c *clientImpl) WatchEndpoints(stopCh <-chan bool) (chan interface{}, chan error, error) { + getURL := c.endpointURL + APIEndpoint + "/endpoints" return c.watch(getURL, stopCh) } @@ -137,13 +143,8 @@ func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, if err != nil { return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) } - stopPods := make(chan bool) - chanPods, chanPodsErr, err := c.WatchPods(stopPods) - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) - } - stopReplicationControllers := make(chan bool) - chanReplicationControllers, chanReplicationControllersErr, err := c.WatchReplicationControllers(stopReplicationControllers) + stopEndpoints := make(chan bool) + chanEndpoints, chanEndpointsErr, err := c.WatchEndpoints(stopEndpoints) if err != nil { return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) } @@ -152,32 +153,26 @@ func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, defer close(errCh) defer close(stopIngresses) defer close(stopServices) - defer close(stopPods) - defer close(stopReplicationControllers) + defer close(stopEndpoints) for { select { case <-stopCh: stopIngresses <- true stopServices <- true - stopPods <- true - stopReplicationControllers <- true + stopEndpoints <- true return case err := <-chanIngressesErr: errCh <- err case err := <-chanServicesErr: errCh <- err - case err := <-chanPodsErr: - errCh <- err - case err := <-chanReplicationControllersErr: + case err := <-chanEndpointsErr: errCh <- err case event := <-chanIngresses: watchCh <- event case event := <-chanServices: watchCh <- event - case event := <-chanPods: - watchCh <- event - case event := <-chanReplicationControllers: + case event := <-chanEndpoints: watchCh <- event } } diff --git a/provider/k8s/endpoints.go b/provider/k8s/endpoints.go new file mode 100644 index 000000000..123ffe36c --- /dev/null +++ b/provider/k8s/endpoints.go @@ -0,0 +1,84 @@ +package k8s + +// Endpoints is a collection of endpoints that implement the actual service. Example: +// Name: "mysvc", +// Subsets: [ +// { +// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], +// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] +// }, +// { +// Addresses: [{"ip": "10.10.3.3"}], +// Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}] +// }, +// ] +type Endpoints struct { + TypeMeta `json:",inline"` + ObjectMeta `json:"metadata,omitempty"` + + // The set of all endpoints is the union of all subsets. + Subsets []EndpointSubset +} + +// EndpointSubset is a group of addresses with a common set of ports. The +// expanded set of endpoints is the Cartesian product of Addresses x Ports. +// For example, given: +// { +// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], +// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] +// } +// The resulting set of endpoints can be viewed as: +// a: [ 10.10.1.1:8675, 10.10.2.2:8675 ], +// b: [ 10.10.1.1:309, 10.10.2.2:309 ] +type EndpointSubset struct { + Addresses []EndpointAddress + NotReadyAddresses []EndpointAddress + Ports []EndpointPort +} + +// EndpointAddress is a tuple that describes single IP address. +type EndpointAddress struct { + // The IP of this endpoint. + // IPv6 is also accepted but not fully supported on all platforms. Also, certain + // kubernetes components, like kube-proxy, are not IPv6 ready. + // TODO: This should allow hostname or IP, see #4447. + IP string + // Optional: Hostname of this endpoint + // Meant to be used by DNS servers etc. + Hostname string `json:"hostname,omitempty"` + // Optional: The kubernetes object related to the entry point. + TargetRef *ObjectReference +} + +// EndpointPort is a tuple that describes a single port. +type EndpointPort struct { + // The name of this port (corresponds to ServicePort.Name). Optional + // if only one port is defined. Must be a DNS_LABEL. + Name string + + // The port number. + Port int32 + + // The IP protocol for this port. + Protocol Protocol +} + +// ObjectReference contains enough information to let you inspect or modify the referred object. +type ObjectReference struct { + Kind string `json:"kind,omitempty"` + Namespace string `json:"namespace,omitempty"` + Name string `json:"name,omitempty"` + UID UID `json:"uid,omitempty"` + APIVersion string `json:"apiVersion,omitempty"` + ResourceVersion string `json:"resourceVersion,omitempty"` + + // Optional. If referring to a piece of an object instead of an entire object, this string + // should contain information to identify the sub-object. For example, if the object + // reference is to a container within a pod, this would take on a value like: + // "spec.containers{name}" (where "name" refers to the name of the container that triggered + // the event) or if no container name is specified "spec.containers[2]" (container with + // index 2 in this pod). This syntax is chosen only to have some well-defined way of + // referencing a part of an object. + // TODO: this design is not final and this field is subject to change in the future. + FieldPath string `json:"fieldPath,omitempty"` +} diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 7362898b2..11556f49c 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -209,9 +209,27 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur if port.Port == 443 { protocol = "https" } - templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{ - URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port), - Weight: 1, + endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace) + if err != nil { + log.Errorf("Error retrieving endpoints: %v", err) + continue + } + if len(endpoints.Subsets) == 0 { + log.Warnf("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(port.Port), + Weight: 1, + } + } else { + for _, subset := range endpoints.Subsets { + for _, address := range subset.Addresses { + url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports)) + templateObjects.Backends[r.Host+pa.Path].Servers[url] = types.Server{ + URL: url, + Weight: 1, + } + } + } } break } @@ -223,6 +241,20 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur return &templateObjects, nil } +func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.EndpointPort) int { + if len(endpointPorts) > 0 { + //name is optional if there is only one port + port := endpointPorts[0] + for _, endpointPort := range endpointPorts { + if servicePort.Name == endpointPort.Name { + port = endpointPort + } + } + return int(port.Port) + } + return servicePort.Port +} + func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool { if servicePort.Port == ingressPort.IntValue() { return true diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index b74b0536d..9f6ddfb32 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -10,6 +10,9 @@ import ( func TestLoadIngresses(t *testing.T) { ingresses := []k8s.Ingress{{ + ObjectMeta: k8s.ObjectMeta{ + Namespace: "testing", + }, Spec: k8s.IngressSpec{ Rules: []k8s.IngressRule{ { @@ -55,23 +58,25 @@ func TestLoadIngresses(t *testing.T) { services := []k8s.Service{ { ObjectMeta: k8s.ObjectMeta{ - Name: "service1", - UID: "1", + Name: "service1", + UID: "1", + Namespace: "testing", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.1", Ports: []k8s.ServicePort{ { Name: "http", - Port: 801, + Port: 80, }, }, }, }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service2", - UID: "2", + Name: "service2", + UID: "2", + Namespace: "testing", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.2", @@ -84,8 +89,9 @@ func TestLoadIngresses(t *testing.T) { }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service3", - UID: "3", + Name: "service3", + UID: "3", + Namespace: "testing", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.3", @@ -98,10 +104,46 @@ func TestLoadIngresses(t *testing.T) { }, }, } + endpoints := []k8s.Endpoints{ + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service1", + UID: "1", + Namespace: "testing", + }, + Subsets: []k8s.EndpointSubset{ + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.10.0.1", + }, + }, + Ports: []k8s.EndpointPort{ + { + Port: 8080, + }, + }, + }, + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.21.0.1", + }, + }, + Ports: []k8s.EndpointPort{ + { + Port: 8080, + }, + }, + }, + }, + }, + } watchChan := make(chan interface{}) client := clientMock{ ingresses: ingresses, services: services, + endpoints: endpoints, watchChan: watchChan, } provider := Kubernetes{} @@ -114,8 +156,12 @@ func TestLoadIngresses(t *testing.T) { Backends: map[string]*types.Backend{ "foo/bar": { Servers: map[string]types.Server{ - "1": { - URL: "http://10.0.0.1:801", + "http://10.10.0.1:8080": { + URL: "http://10.10.0.1:8080", + Weight: 1, + }, + "http://10.21.0.1:8080": { + URL: "http://10.21.0.1:8080", Weight: 1, }, }, @@ -1150,6 +1196,7 @@ func TestHostlessIngress(t *testing.T) { type clientMock struct { ingresses []k8s.Ingress services []k8s.Service + endpoints []k8s.Endpoints watchChan chan interface{} } @@ -1174,6 +1221,16 @@ func (c clientMock) GetServices(predicate func(k8s.Service) bool) ([]k8s.Service } return services, nil } + +func (c clientMock) GetEndpoints(name, namespace string) (k8s.Endpoints, error) { + for _, endpoints := range c.endpoints { + if endpoints.Namespace == namespace && endpoints.Name == name { + return endpoints, nil + } + } + return k8s.Endpoints{}, nil +} + func (c clientMock) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) { return c.watchChan, make(chan error), nil } From 6accb90c4769f9de24a73ddf7f56e3058cc460d6 Mon Sep 17 00:00:00 2001 From: Ed Robinson Date: Thu, 26 May 2016 00:53:51 +0100 Subject: [PATCH 02/19] Simplify Service Lookup Since we already know the name and namespace of the service(s) we want we can just get the correct one back from the API without filtering the results. --- provider/k8s/client.go | 27 +++++++---------- provider/kubernetes.go | 59 ++++++++++++++++--------------------- provider/kubernetes_test.go | 9 +++--- 3 files changed, 41 insertions(+), 54 deletions(-) diff --git a/provider/k8s/client.go b/provider/k8s/client.go index 930c9835e..0b2195615 100644 --- a/provider/k8s/client.go +++ b/provider/k8s/client.go @@ -16,12 +16,13 @@ const ( APIEndpoint = "/api/v1" extentionsEndpoint = "/apis/extensions/v1beta1" defaultIngress = "/ingresses" + namespaces = "/namespaces/" ) // Client is a client for the Kubernetes master. type Client interface { GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) - GetServices(predicate func(Service) bool) ([]Service, error) + GetService(name, namespace string) (Service, error) GetEndpoints(name, namespace string) (Endpoints, error) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) } @@ -77,26 +78,20 @@ func (c *clientImpl) WatchIngresses(stopCh <-chan bool) (chan interface{}, chan return c.watch(getURL, stopCh) } -// GetServices returns all services in the cluster -func (c *clientImpl) GetServices(predicate func(Service) bool) ([]Service, error) { - getURL := c.endpointURL + APIEndpoint + "/services" +// GetService returns the named service from the named namespace +func (c *clientImpl) GetService(name, namespace string) (Service, error) { + getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/services/" + name body, err := c.do(c.request(getURL)) if err != nil { - return nil, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err) + return Service{}, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err) } - var serviceList ServiceList - if err := json.Unmarshal(body, &serviceList); err != nil { - return nil, fmt.Errorf("failed to decode list of services resources: %v", err) + var service Service + if err := json.Unmarshal(body, &service); err != nil { + return Service{}, fmt.Errorf("failed to decode service resource: %v", err) } - services := serviceList.Items[:0] - for _, service := range serviceList.Items { - if predicate(service) { - services = append(services, service) - } - } - return services, nil + return service, nil } // WatchServices returns all services in the cluster @@ -108,7 +103,7 @@ func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan e // GetEndpoints returns the named Endpoints // Endpoints have the same name as the coresponding service func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) { - getURL := c.endpointURL + APIEndpoint + "/namespaces/" + namespace + "/endpoints/" + name + getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/endpoints/" + name body, err := c.do(c.request(getURL)) if err != nil { diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 11556f49c..abb527af6 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -190,49 +190,42 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur Rule: ruleType + ":" + pa.Path, } } - services, err := k8sClient.GetServices(func(service k8s.Service) bool { - return service.ObjectMeta.Namespace == i.ObjectMeta.Namespace && service.Name == pa.Backend.ServiceName - }) + service, err := k8sClient.GetService(pa.Backend.ServiceName, i.ObjectMeta.Namespace) if err != nil { log.Warnf("Error retrieving services: %v", err) - continue - } - if len(services) == 0 { - // no backends found, delete frontend... delete(templateObjects.Frontends, r.Host+pa.Path) log.Warnf("Error retrieving services %s", pa.Backend.ServiceName) + continue } - for _, service := range services { - protocol := "http" - for _, port := range service.Spec.Ports { - if equalPorts(port, pa.Backend.ServicePort) { - if port.Port == 443 { - protocol = "https" + protocol := "http" + for _, port := range service.Spec.Ports { + if equalPorts(port, pa.Backend.ServicePort) { + if port.Port == 443 { + protocol = "https" + } + endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace) + if err != nil { + log.Errorf("Error retrieving endpoints: %v", err) + continue + } + if len(endpoints.Subsets) == 0 { + log.Warnf("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(port.Port), + Weight: 1, } - endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace) - if err != nil { - log.Errorf("Error retrieving endpoints: %v", err) - continue - } - if len(endpoints.Subsets) == 0 { - log.Warnf("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(port.Port), - Weight: 1, - } - } else { - for _, subset := range endpoints.Subsets { - for _, address := range subset.Addresses { - url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports)) - templateObjects.Backends[r.Host+pa.Path].Servers[url] = types.Server{ - URL: url, - Weight: 1, - } + } else { + for _, subset := range endpoints.Subsets { + for _, address := range subset.Addresses { + url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports)) + templateObjects.Backends[r.Host+pa.Path].Servers[url] = types.Server{ + URL: url, + Weight: 1, } } } - break } + break } } } diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index 9f6ddfb32..39a592f1b 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -1212,14 +1212,13 @@ func (c clientMock) GetIngresses(predicate func(k8s.Ingress) bool) ([]k8s.Ingres func (c clientMock) WatchIngresses(predicate func(k8s.Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) { return c.watchChan, make(chan error), nil } -func (c clientMock) GetServices(predicate func(k8s.Service) bool) ([]k8s.Service, error) { - var services []k8s.Service +func (c clientMock) GetService(name, namespace string) (k8s.Service, error) { for _, service := range c.services { - if predicate(service) { - services = append(services, service) + if service.Namespace == namespace && service.Name == name { + return service, nil } } - return services, nil + return k8s.Service{}, nil } func (c clientMock) GetEndpoints(name, namespace string) (k8s.Endpoints, error) { From 2e735f622f6b51e0d8dd95984cdf59e5f832dd9d Mon Sep 17 00:00:00 2001 From: Ed Robinson Date: Thu, 26 May 2016 12:09:36 +0100 Subject: [PATCH 03/19] Adds some more coverage of the endpoint port selection logic. --- provider/kubernetes_test.go | 60 +++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index 39a592f1b..2e31a7df9 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -24,7 +24,7 @@ func TestLoadIngresses(t *testing.T) { Path: "/bar", Backend: k8s.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromString("http"), + ServicePort: k8s.FromInt(80), }, }, }, @@ -39,7 +39,7 @@ func TestLoadIngresses(t *testing.T) { { Backend: k8s.IngressBackend{ ServiceName: "service3", - ServicePort: k8s.FromInt(443), + ServicePort: k8s.FromString("https"), }, }, { @@ -66,7 +66,6 @@ func TestLoadIngresses(t *testing.T) { ClusterIP: "10.0.0.1", Ports: []k8s.ServicePort{ { - Name: "http", Port: 80, }, }, @@ -98,6 +97,10 @@ func TestLoadIngresses(t *testing.T) { Ports: []k8s.ServicePort{ { Name: "http", + Port: 80, + }, + { + Name: "https", Port: 443, }, }, @@ -138,6 +141,49 @@ func TestLoadIngresses(t *testing.T) { }, }, }, + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service3", + UID: "3", + Namespace: "testing", + }, + Subsets: []k8s.EndpointSubset{ + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.15.0.1", + }, + }, + Ports: []k8s.EndpointPort{ + { + Name: "http", + Port: 8080, + }, + { + Name: "https", + Port: 8443, + }, + }, + }, + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.15.0.2", + }, + }, + Ports: []k8s.EndpointPort{ + { + Name: "http", + Port: 9080, + }, + { + Name: "https", + Port: 9443, + }, + }, + }, + }, + }, } watchChan := make(chan interface{}) client := clientMock{ @@ -174,8 +220,12 @@ func TestLoadIngresses(t *testing.T) { URL: "http://10.0.0.2:802", Weight: 1, }, - "3": { - URL: "https://10.0.0.3:443", + "https://10.15.0.1:8443": { + URL: "https://10.15.0.1:8443", + Weight: 1, + }, + "https://10.15.0.2:9443": { + URL: "https://10.15.0.2:9443", Weight: 1, }, }, From fe0a8f3363d3cae4785b18b9293f5984f8ea35f2 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 3 May 2016 16:52:14 +0200 Subject: [PATCH 04/19] Flaeg integration --- cmd.go | 230 -------------------------------- configuration.go | 266 ++++++++++++++++++------------------- flaeg_test.go | 91 +++++++++++++ provider/boltdb.go | 2 +- provider/consul.go | 2 +- provider/consul_catalog.go | 4 +- provider/docker.go | 14 +- provider/etcd.go | 2 +- provider/file.go | 2 +- provider/kubernetes.go | 11 +- provider/kv.go | 16 +-- provider/marathon.go | 10 +- provider/provider.go | 4 +- provider/zk.go | 2 +- traefik.go | 97 +++++++++++++- web.go | 9 +- 16 files changed, 361 insertions(+), 401 deletions(-) delete mode 100644 cmd.go create mode 100644 flaeg_test.go diff --git a/cmd.go b/cmd.go deleted file mode 100644 index 6580d6a15..000000000 --- a/cmd.go +++ /dev/null @@ -1,230 +0,0 @@ -/* -Copyright -*/ -package main - -import ( - "encoding/json" - fmtlog "log" - "os" - "strings" - "time" - - "net/http" - - log "github.com/Sirupsen/logrus" - "github.com/containous/traefik/middlewares" - "github.com/containous/traefik/provider" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var traefikCmd = &cobra.Command{ - Use: "traefik", - Short: "traefik, a modern reverse proxy", - Long: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. -Complete documentation is available at http://traefik.io`, - Run: func(cmd *cobra.Command, args []string) { - run() - }, -} -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print version", - Long: `Print version`, - Run: func(cmd *cobra.Command, args []string) { - fmtlog.Println(Version + " built on the " + BuildDate) - os.Exit(0) - }, -} - -var arguments = struct { - GlobalConfiguration - web bool - file bool - docker bool - dockerTLS bool - marathon bool - consul bool - consulTLS bool - consulCatalog bool - zookeeper bool - etcd bool - etcdTLS bool - boltdb bool - kubernetes bool -}{ - GlobalConfiguration{ - EntryPoints: make(EntryPoints), - Docker: &provider.Docker{ - TLS: &provider.DockerTLS{}, - }, - File: &provider.File{}, - Web: &WebProvider{}, - Marathon: &provider.Marathon{}, - Consul: &provider.Consul{ - Kv: provider.Kv{ - TLS: &provider.KvTLS{}, - }, - }, - ConsulCatalog: &provider.ConsulCatalog{}, - Zookeeper: &provider.Zookepper{}, - Etcd: &provider.Etcd{ - Kv: provider.Kv{ - TLS: &provider.KvTLS{}, - }, - }, - Boltdb: &provider.BoltDb{}, - Kubernetes: &provider.Kubernetes{}, - }, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, -} - -func init() { - traefikCmd.AddCommand(versionCmd) - traefikCmd.PersistentFlags().StringP("configFile", "c", "", "Configuration file to use (TOML).") - traefikCmd.PersistentFlags().BoolVarP(&arguments.Debug, "debug", "d", false, "Enable debug mode") - traefikCmd.PersistentFlags().StringP("graceTimeOut", "g", "10", "Timeout in seconds. Duration to give active requests a chance to finish during hot-reloads") - traefikCmd.PersistentFlags().String("accessLogsFile", "log/access.log", "Access logs file") - traefikCmd.PersistentFlags().String("traefikLogsFile", "log/traefik.log", "Traefik logs file") - traefikCmd.PersistentFlags().Var(&arguments.EntryPoints, "entryPoints", "Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'") - traefikCmd.PersistentFlags().Var(&arguments.DefaultEntryPoints, "defaultEntryPoints", "Entrypoints to be used by frontends that do not specify any entrypoint") - traefikCmd.PersistentFlags().StringP("logLevel", "l", "ERROR", "Log level") - traefikCmd.PersistentFlags().DurationVar(&arguments.ProvidersThrottleDuration, "providersThrottleDuration", time.Duration(2*time.Second), "Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time.") - traefikCmd.PersistentFlags().Int("maxIdleConnsPerHost", 0, "If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used") - - traefikCmd.PersistentFlags().BoolVar(&arguments.web, "web", false, "Enable Web backend") - traefikCmd.PersistentFlags().StringVar(&arguments.Web.Address, "web.address", ":8080", "Web administration port") - traefikCmd.PersistentFlags().StringVar(&arguments.Web.CertFile, "web.cerFile", "", "SSL certificate") - traefikCmd.PersistentFlags().StringVar(&arguments.Web.KeyFile, "web.keyFile", "", "SSL certificate") - traefikCmd.PersistentFlags().BoolVar(&arguments.Web.ReadOnly, "web.readOnly", false, "Enable read only API") - - traefikCmd.PersistentFlags().BoolVar(&arguments.file, "file", false, "Enable File backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.File.Watch, "file.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.File.Filename, "file.filename", "", "Override default configuration template. For advanced users :)") - - traefikCmd.PersistentFlags().BoolVar(&arguments.docker, "docker", false, "Enable Docker backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.Docker.Watch, "docker.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Filename, "docker.filename", "", "Override default configuration template. For advanced users :)") - traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Endpoint, "docker.endpoint", "unix:///var/run/docker.sock", "Docker server endpoint. Can be a tcp or a unix socket endpoint") - traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Domain, "docker.domain", "", "Default domain used") - traefikCmd.PersistentFlags().BoolVar(&arguments.dockerTLS, "docker.tls", false, "Enable Docker TLS support") - traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.CA, "docker.tls.ca", "", "TLS CA") - traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.Cert, "docker.tls.cert", "", "TLS cert") - traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.Key, "docker.tls.key", "", "TLS key") - traefikCmd.PersistentFlags().BoolVar(&arguments.Docker.TLS.InsecureSkipVerify, "docker.tls.insecureSkipVerify", false, "TLS insecure skip verify") - - traefikCmd.PersistentFlags().BoolVar(&arguments.marathon, "marathon", false, "Enable Marathon backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.Marathon.Watch, "marathon.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Filename, "marathon.filename", "", "Override default configuration template. For advanced users :)") - traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Endpoint, "marathon.endpoint", "http://127.0.0.1:8080", "Marathon server endpoint. You can also specify multiple endpoint for Marathon") - traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Domain, "marathon.domain", "", "Default domain used") - traefikCmd.PersistentFlags().BoolVar(&arguments.Marathon.ExposedByDefault, "marathon.exposedByDefault", true, "Expose Marathon apps by default") - - traefikCmd.PersistentFlags().BoolVar(&arguments.consul, "consul", false, "Enable Consul backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.Watch, "consul.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Filename, "consul.filename", "", "Override default configuration template. For advanced users :)") - traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Endpoint, "consul.endpoint", "127.0.0.1:8500", "Comma sepparated Consul server endpoints") - traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Prefix, "consul.prefix", "/traefik", "Prefix used for KV store") - traefikCmd.PersistentFlags().BoolVar(&arguments.consulTLS, "consul.tls", false, "Enable Consul TLS support") - traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.CA, "consul.tls.ca", "", "TLS CA") - traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Cert, "consul.tls.cert", "", "TLS cert") - traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Key, "consul.tls.key", "", "TLS key") - traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.TLS.InsecureSkipVerify, "consul.tls.insecureSkipVerify", false, "TLS insecure skip verify") - - traefikCmd.PersistentFlags().BoolVar(&arguments.consulCatalog, "consulCatalog", false, "Enable Consul catalog backend") - traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Domain, "consulCatalog.domain", "", "Default domain used") - traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Endpoint, "consulCatalog.endpoint", "127.0.0.1:8500", "Consul server endpoint") - traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Prefix, "consulCatalog.prefix", "traefik", "Consul catalog tag prefix") - - traefikCmd.PersistentFlags().BoolVar(&arguments.zookeeper, "zookeeper", false, "Enable Zookeeper backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.Zookeeper.Watch, "zookeeper.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Filename, "zookeeper.filename", "", "Override default configuration template. For advanced users :)") - traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Endpoint, "zookeeper.endpoint", "127.0.0.1:2181", "Comma sepparated Zookeeper server endpoints") - traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Prefix, "zookeeper.prefix", "/traefik", "Prefix used for KV store") - - traefikCmd.PersistentFlags().BoolVar(&arguments.etcd, "etcd", false, "Enable Etcd backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.Watch, "etcd.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Filename, "etcd.filename", "", "Override default configuration template. For advanced users :)") - traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Endpoint, "etcd.endpoint", "127.0.0.1:4001", "Comma sepparated Etcd server endpoints") - traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Prefix, "etcd.prefix", "/traefik", "Prefix used for KV store") - traefikCmd.PersistentFlags().BoolVar(&arguments.etcdTLS, "etcd.tls", false, "Enable Etcd TLS support") - traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.CA, "etcd.tls.ca", "", "TLS CA") - traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Cert, "etcd.tls.cert", "", "TLS cert") - traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Key, "etcd.tls.key", "", "TLS key") - traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.TLS.InsecureSkipVerify, "etcd.tls.insecureSkipVerify", false, "TLS insecure skip verify") - - traefikCmd.PersistentFlags().BoolVar(&arguments.boltdb, "boltdb", false, "Enable Boltdb backend") - traefikCmd.PersistentFlags().BoolVar(&arguments.Boltdb.Watch, "boltdb.watch", true, "Watch provider") - traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Filename, "boltdb.filename", "", "Override default configuration template. For advanced users :)") - traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Endpoint, "boltdb.endpoint", "127.0.0.1:4001", "Boltdb server endpoint") - traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Prefix, "boltdb.prefix", "/traefik", "Prefix used for KV store") - - traefikCmd.PersistentFlags().BoolVar(&arguments.kubernetes, "kubernetes", false, "Enable Kubernetes backend") - traefikCmd.PersistentFlags().StringVar(&arguments.Kubernetes.Endpoint, "kubernetes.endpoint", "http://127.0.0.1:8080", "Kubernetes server endpoint") - traefikCmd.PersistentFlags().StringSliceVar(&arguments.Kubernetes.Namespaces, "kubernetes.namespaces", []string{}, "Kubernetes namespaces") - - _ = viper.BindPFlag("configFile", traefikCmd.PersistentFlags().Lookup("configFile")) - _ = viper.BindPFlag("graceTimeOut", traefikCmd.PersistentFlags().Lookup("graceTimeOut")) - _ = viper.BindPFlag("logLevel", traefikCmd.PersistentFlags().Lookup("logLevel")) - _ = viper.BindPFlag("debug", traefikCmd.PersistentFlags().Lookup("debug")) - // TODO: wait for this issue to be corrected: https://github.com/spf13/viper/issues/105 - _ = viper.BindPFlag("providersThrottleDuration", traefikCmd.PersistentFlags().Lookup("providersThrottleDuration")) - _ = viper.BindPFlag("maxIdleConnsPerHost", traefikCmd.PersistentFlags().Lookup("maxIdleConnsPerHost")) - viper.SetDefault("providersThrottleDuration", time.Duration(2*time.Second)) - viper.SetDefault("logLevel", "ERROR") - viper.SetDefault("MaxIdleConnsPerHost", 200) -} - -func run() { - fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) - - // load global configuration - globalConfiguration := LoadConfiguration() - - http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost - loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile) - defer loggerMiddleware.Close() - - // logging - level, err := log.ParseLevel(strings.ToLower(globalConfiguration.LogLevel)) - if err != nil { - log.Fatal("Error getting level", err) - } - log.SetLevel(level) - - if len(globalConfiguration.TraefikLogsFile) > 0 { - fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - defer func() { - if err := fi.Close(); err != nil { - log.Error("Error closinf file", err) - } - }() - if err != nil { - log.Fatal("Error opening file", err) - } else { - log.SetOutput(fi) - log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true}) - } - } else { - log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true}) - } - jsonConf, _ := json.Marshal(globalConfiguration) - log.Debugf("Global configuration loaded %s", string(jsonConf)) - server := NewServer(*globalConfiguration) - server.Start() - defer server.Close() - log.Info("Shutting down") -} diff --git a/configuration.go b/configuration.go index bae48d7cb..9b39d5db6 100644 --- a/configuration.go +++ b/configuration.go @@ -3,42 +3,44 @@ package main import ( "errors" "fmt" - fmtlog "log" - "regexp" - "strings" - "time" - "github.com/containous/traefik/acme" "github.com/containous/traefik/provider" "github.com/containous/traefik/types" - "github.com/mitchellh/mapstructure" - "github.com/spf13/viper" + "regexp" + "strings" + "time" ) +// TraefikConfiguration holds GlobalConfiguration and other stuff +type TraefikConfiguration struct { + GlobalConfiguration + ConfigFile string `short:"c" description:"Timeout in seconds. Duration to give active requests a chance to finish during hot-reloads"` +} + // GlobalConfiguration holds global configuration (with providers, etc.). // It's populated from the traefik configuration file passed as an argument to the binary. type GlobalConfiguration struct { - GraceTimeOut int64 + GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."` Debug bool - AccessLogsFile string - TraefikLogsFile string - LogLevel string - EntryPoints EntryPoints + AccessLogsFile string `description:"Access logs file"` + TraefikLogsFile string `description:"Traefik logs file"` + LogLevel string `short:"l" description:"Log level"` + EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'"` ACME *acme.ACME - DefaultEntryPoints DefaultEntryPoints - ProvidersThrottleDuration time.Duration - MaxIdleConnsPerHost int + DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"` + ProvidersThrottleDuration time.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time."` + MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"` Retry *Retry - Docker *provider.Docker - File *provider.File - Web *WebProvider - Marathon *provider.Marathon - Consul *provider.Consul - ConsulCatalog *provider.ConsulCatalog - Etcd *provider.Etcd - Zookeeper *provider.Zookepper - Boltdb *provider.BoltDb - Kubernetes *provider.Kubernetes + Docker *provider.Docker `description:"Enable Docker backend"` + File *provider.File `description:"Enable File backend"` + Web *WebProvider `description:"Enable Web backend"` + Marathon *provider.Marathon `description:"Enable Marathon backend"` + Consul *provider.Consul `description:"Enable Consul backend"` + ConsulCatalog *provider.ConsulCatalog `description:"Enable Consul catalog backend"` + Etcd *provider.Etcd `description:"Enable Etcd backend"` + Zookeeper *provider.Zookepper `description:"Enable Zookeeper backend"` + Boltdb *provider.BoltDb `description:"Enable Boltdb backend"` + Kubernetes *provider.Kubernetes `description:"Enable Kubernetes backend"` } // DefaultEntryPoints holds default entry points @@ -47,6 +49,7 @@ type DefaultEntryPoints []string // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. func (dep *DefaultEntryPoints) String() string { + //TODO : return fmt.Sprintf("%#v", dep) } @@ -64,6 +67,14 @@ func (dep *DefaultEntryPoints) Set(value string) error { return nil } +// Get return the EntryPoints map +func (dep *DefaultEntryPoints) Get() interface{} { return DefaultEntryPoints(*dep) } + +// SetValue sets the EntryPoints map with val +func (dep *DefaultEntryPoints) SetValue(val interface{}) { + *dep = DefaultEntryPoints(val.(DefaultEntryPoints)) +} + // Type is type of the struct func (dep *DefaultEntryPoints) Type() string { return fmt.Sprint("defaultentrypoints²") @@ -75,6 +86,7 @@ type EntryPoints map[string]*EntryPoint // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. func (ep *EntryPoints) String() string { + //TODO : return "" } @@ -122,6 +134,14 @@ func (ep *EntryPoints) Set(value string) error { return nil } +// Get return the EntryPoints map +func (ep *EntryPoints) Get() interface{} { return EntryPoints(*ep) } + +// SetValue sets the EntryPoints map with val +func (ep *EntryPoints) SetValue(val interface{}) { + *ep = EntryPoints(val.(EntryPoints)) +} + // Type is type of the struct func (ep *EntryPoints) Type() string { return fmt.Sprint("entrypoints²") @@ -154,6 +174,7 @@ type Certificates []Certificate // The String method's output will be used in diagnostics. func (certs *Certificates) String() string { if len(*certs) == 0 { + //TODO : return "" } return (*certs)[0].CertFile + "," + (*certs)[0].KeyFile @@ -191,117 +212,96 @@ type Retry struct { MaxMem int64 } -// NewGlobalConfiguration returns a GlobalConfiguration with default values. -func NewGlobalConfiguration() *GlobalConfiguration { - return new(GlobalConfiguration) +// NewTraefikPointersConfiguration creates a TraefikConfiguration with pointers default values +func NewTraefikPointersConfiguration() *TraefikConfiguration { + //default Docker + var defaultDocker provider.Docker + defaultDocker.Watch = true + defaultDocker.Endpoint = "unix:///var/run/docker.sock" + defaultDocker.TLS = &provider.DockerTLS{} + + // default File + var defaultFile provider.File + defaultFile.Watch = true + defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed() + + // default Web + var defaultWeb WebProvider + defaultWeb.Address = ":8080" + + // default Marathon + var defaultMarathon provider.Marathon + defaultMarathon.Watch = true + defaultMarathon.Endpoint = "http://127.0.0.1:8080" + defaultMarathon.ExposedByDefault = true + + // default Consul + var defaultConsul provider.Consul + defaultConsul.Watch = true + defaultConsul.Endpoint = "127.0.0.1:8500" + defaultConsul.Prefix = "/traefik" + defaultConsul.TLS = &provider.KvTLS{} + + // default ConsulCatalog + var defaultConsulCatalog provider.ConsulCatalog + defaultConsulCatalog.Endpoint = "127.0.0.1:8500" + + // default Etcd + var defaultEtcd provider.Etcd + defaultEtcd.Watch = true + defaultEtcd.Endpoint = "127.0.0.1:400" + defaultEtcd.Prefix = "/traefik" + defaultEtcd.TLS = &provider.KvTLS{} + + //default Zookeeper + var defaultZookeeper provider.Zookepper + defaultZookeeper.Watch = true + defaultZookeeper.Endpoint = "127.0.0.1:2181" + defaultZookeeper.Prefix = "/traefik" + + //default Boltdb + var defaultBoltDb provider.BoltDb + defaultBoltDb.Watch = true + defaultBoltDb.Endpoint = "127.0.0.1:4001" + defaultBoltDb.Prefix = "/traefik" + + //default Kubernetes + var defaultKubernetes provider.Kubernetes + defaultKubernetes.Watch = true + defaultKubernetes.Endpoint = "127.0.0.1:8080" + + defaultConfiguration := GlobalConfiguration{ + Docker: &defaultDocker, + File: &defaultFile, + Web: &defaultWeb, + Marathon: &defaultMarathon, + Consul: &defaultConsul, + ConsulCatalog: &defaultConsulCatalog, + Etcd: &defaultEtcd, + Zookeeper: &defaultZookeeper, + Boltdb: &defaultBoltDb, + Kubernetes: &defaultKubernetes, + } + return &TraefikConfiguration{ + GlobalConfiguration: defaultConfiguration, + } } -// LoadConfiguration returns a GlobalConfiguration. -func LoadConfiguration() *GlobalConfiguration { - configuration := NewGlobalConfiguration() - viper.SetEnvPrefix("traefik") - viper.SetConfigType("toml") - viper.AutomaticEnv() - if len(viper.GetString("configFile")) > 0 { - viper.SetConfigFile(viper.GetString("configFile")) - } else { - viper.SetConfigName("traefik") // name of config file (without extension) +// NewTraefikConfiguration creates a TraefikConfiguration with default values +func NewTraefikConfiguration() *TraefikConfiguration { + return &TraefikConfiguration{ + GlobalConfiguration: GlobalConfiguration{ + GraceTimeOut: 10, + AccessLogsFile: "log/access.log", + TraefikLogsFile: "log/traefik.log", + LogLevel: "ERROR", + EntryPoints: map[string]*EntryPoint{"http": &EntryPoint{Address: ":80"}}, + DefaultEntryPoints: []string{"http"}, + ProvidersThrottleDuration: time.Duration(2 * time.Second), + MaxIdleConnsPerHost: 200, + }, + ConfigFile: "", } - viper.AddConfigPath("/etc/traefik/") // path to look for the config file in - viper.AddConfigPath("$HOME/.traefik/") // call multiple times to add many search paths - viper.AddConfigPath(".") // optionally look for config in the working directory - if err := viper.ReadInConfig(); err != nil { - if len(viper.ConfigFileUsed()) > 0 { - fmtlog.Printf("Error reading configuration file: %s", err) - } else { - fmtlog.Printf("No configuration file found") - } - } - - if len(arguments.EntryPoints) > 0 { - viper.Set("entryPoints", arguments.EntryPoints) - } - if len(arguments.DefaultEntryPoints) > 0 { - viper.Set("defaultEntryPoints", arguments.DefaultEntryPoints) - } - if arguments.web { - viper.Set("web", arguments.Web) - } - if arguments.file { - viper.Set("file", arguments.File) - } - if !arguments.dockerTLS { - arguments.Docker.TLS = nil - } - if arguments.docker { - viper.Set("docker", arguments.Docker) - } - if arguments.marathon { - viper.Set("marathon", arguments.Marathon) - } - if !arguments.consulTLS { - arguments.Consul.TLS = nil - } - if arguments.consul { - viper.Set("consul", arguments.Consul) - } - if arguments.consulCatalog { - viper.Set("consulCatalog", arguments.ConsulCatalog) - } - if arguments.zookeeper { - viper.Set("zookeeper", arguments.Zookeeper) - } - if !arguments.etcdTLS { - arguments.Etcd.TLS = nil - } - if arguments.etcd { - viper.Set("etcd", arguments.Etcd) - } - if arguments.boltdb { - viper.Set("boltdb", arguments.Boltdb) - } - if arguments.kubernetes { - viper.Set("kubernetes", arguments.Kubernetes) - } - if err := unmarshal(&configuration); err != nil { - - fmtlog.Fatalf("Error reading file: %s", err) - } - - if len(configuration.EntryPoints) == 0 { - configuration.EntryPoints = make(map[string]*EntryPoint) - configuration.EntryPoints["http"] = &EntryPoint{ - Address: ":80", - } - configuration.DefaultEntryPoints = []string{"http"} - } - - if configuration.File != nil && len(configuration.File.Filename) == 0 { - // no filename, setting to global config file - configuration.File.Filename = viper.ConfigFileUsed() - } - - return configuration -} - -func unmarshal(rawVal interface{}) error { - config := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), - Metadata: nil, - Result: rawVal, - WeaklyTypedInput: true, - } - - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return err - } - - err = decoder.Decode(viper.AllSettings()) - if err != nil { - return err - } - return nil } type configs map[string]*types.Configuration diff --git a/flaeg_test.go b/flaeg_test.go new file mode 100644 index 000000000..bbf36c42d --- /dev/null +++ b/flaeg_test.go @@ -0,0 +1,91 @@ +package main + +import ( + "github.com/cocap10/flaeg" + "reflect" + "testing" + "time" +) + +func TestLoad(t *testing.T) { + var configuration GlobalConfiguration + defaultConfiguration := NewGlobalConfiguration() + args := []string{ + // "-h", + "--docker", + "--file", + "--web", + "--marathon", + "--consul", + "--consulcatalog", + "--etcd", + "--zookeeper", + "--boltdb", + } + if err := flaeg.Load(&configuration, defaultConfiguration, args); err != nil { + t.Fatalf("Error: %s", err) + } + // fmt.Printf("result : \n%+v\n", configuration) + if !reflect.DeepEqual(configuration, *defaultConfiguration) { + t.Fatalf("\nexpected\t: %+v\ngot\t\t\t: %+v", *defaultConfiguration, configuration) + } +} + +func TestLoadWithParsers(t *testing.T) { + var configuration GlobalConfiguration + defaultConfiguration := NewGlobalConfiguration() + args := []string{ + // "-h", + "--docker", + // "--file", + "--web.address=:8888", + "--marathon", + "--consul", + "--consulcatalog", + "--etcd.tls.insecureskipverify", + "--zookeeper", + "--boltdb", + "--accesslogsfile=log2/access.log", + "--entrypoints=Name:http Address::8000 Redirect.EntryPoint:https", + "--entrypoints=Name:https Address::8443 Redirect.EntryPoint:http", + "--defaultentrypoints=https", + "--defaultentrypoints=ssh", + "--providersthrottleduration=4s", + } + parsers := map[reflect.Type]flaeg.Parser{} + var defaultEntryPointsParser DefaultEntryPoints + parsers[reflect.TypeOf(DefaultEntryPoints{})] = &defaultEntryPointsParser + entryPointsParser := EntryPoints{} + parsers[reflect.TypeOf(EntryPoints{})] = &entryPointsParser + + if err := flaeg.LoadWithParsers(&configuration, defaultConfiguration, args, parsers); err != nil { + t.Fatalf("Error: %s", err) + } + // fmt.Printf("result : \n%+v\n", configuration) + + //Check + check := *defaultConfiguration + check.File = nil + check.Web.Address = ":8888" + check.AccessLogsFile = "log2/access.log" + check.Etcd.TLS.InsecureSkipVerify = true + check.EntryPoints = make(map[string]*EntryPoint) + check.EntryPoints["http"] = &EntryPoint{ + Address: ":8000", + Redirect: &Redirect{ + EntryPoint: "https", + }, + } + check.EntryPoints["https"] = &EntryPoint{ + Address: ":8443", + Redirect: &Redirect{ + EntryPoint: "http", + }, + } + check.DefaultEntryPoints = []string{"https", "ssh"} + check.ProvidersThrottleDuration = time.Duration(4 * time.Second) + + if !reflect.DeepEqual(&configuration, &check) { + t.Fatalf("\nexpected\t: %+v\ngot\t\t\t: %+v", check, configuration) + } +} diff --git a/provider/boltdb.go b/provider/boltdb.go index 0f941627e..966a4b8da 100644 --- a/provider/boltdb.go +++ b/provider/boltdb.go @@ -9,7 +9,7 @@ import ( // BoltDb holds configurations of the BoltDb provider. type BoltDb struct { - Kv `mapstructure:",squash"` + Kv `mapstructure:",squash" description:"go through"` } // Provide allows the provider to provide configurations to traefik diff --git a/provider/consul.go b/provider/consul.go index a2df6852b..d6d81f3e7 100644 --- a/provider/consul.go +++ b/provider/consul.go @@ -9,7 +9,7 @@ import ( // Consul holds configurations of the Consul provider. type Consul struct { - Kv `mapstructure:",squash"` + Kv `mapstructure:",squash" description:"go through"` } // Provide allows the provider to provide configurations to traefik diff --git a/provider/consul_catalog.go b/provider/consul_catalog.go index 90606e9d4..c17cfe0a9 100644 --- a/provider/consul_catalog.go +++ b/provider/consul_catalog.go @@ -24,8 +24,8 @@ const ( // ConsulCatalog holds configurations of the Consul catalog provider. type ConsulCatalog struct { BaseProvider `mapstructure:",squash"` - Endpoint string - Domain string + Endpoint string `description:"Consul server endpoint"` + Domain string `description:"Default domain used"` client *api.Client Prefix string } diff --git a/provider/docker.go b/provider/docker.go index 7169f20e1..55b73f8b6 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -30,17 +30,17 @@ const DockerAPIVersion string = "1.21" // Docker holds configurations of the Docker provider. type Docker struct { BaseProvider `mapstructure:",squash"` - Endpoint string - Domain string - TLS *DockerTLS + Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` + Domain string `description:"Default domain used"` + TLS *DockerTLS `description:"Enable Docker TLS support"` } // DockerTLS holds TLS specific configurations type DockerTLS struct { - CA string - Cert string - Key string - InsecureSkipVerify bool + CA string `description:"TLS CA"` + Cert string `description:"TLS cert"` + Key string `description:"TLS key"` + InsecureSkipVerify bool `description:"TLS insecure skip verify"` } func (provider *Docker) createClient() (client.APIClient, error) { diff --git a/provider/etcd.go b/provider/etcd.go index 3d0b9e428..3344245e4 100644 --- a/provider/etcd.go +++ b/provider/etcd.go @@ -9,7 +9,7 @@ import ( // Etcd holds configurations of the Etcd provider. type Etcd struct { - Kv `mapstructure:",squash"` + Kv `mapstructure:",squash" description:"go through"` } // Provide allows the provider to provide configurations to traefik diff --git a/provider/file.go b/provider/file.go index 6b943b199..c5597ec19 100644 --- a/provider/file.go +++ b/provider/file.go @@ -14,7 +14,7 @@ import ( // File holds configurations of the File provider. type File struct { - BaseProvider `mapstructure:",squash"` + BaseProvider `mapstructure:",squash" description:"go through"` } // Provide allows the provider to provide configurations to traefik diff --git a/provider/kubernetes.go b/provider/kubernetes.go index abb527af6..bdaf245ce 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -20,12 +20,15 @@ const ( serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" ) +// Namespaces holds kubernetes namespaces +type Namespaces []string + // Kubernetes holds configurations of the Kubernetes provider. type Kubernetes struct { BaseProvider `mapstructure:",squash"` - Endpoint string - disablePassHostHeaders bool - Namespaces []string + Endpoint string `description:"Kubernetes server endpoint"` + DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"` + Namespaces Namespaces `description:"Kubernetes namespaces"` } func (provider *Kubernetes) createClient() (k8s.Client, error) { @@ -259,7 +262,7 @@ func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool { } func (provider *Kubernetes) getPassHostHeader() bool { - if provider.disablePassHostHeaders { + if provider.DisablePassHostHeaders { return false } return true diff --git a/provider/kv.go b/provider/kv.go index b257fd71e..852799dcb 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -22,20 +22,20 @@ import ( // Kv holds common configurations of key-value providers. type Kv struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - Prefix string - TLS *KvTLS + BaseProvider `mapstructure:",squash" description:"go through"` + Endpoint string `description:"Comma sepparated server endpoints"` + Prefix string `description:"Prefix used for KV store"` + TLS *KvTLS `description:"Enable TLS support"` storeType store.Backend kvclient store.Store } // KvTLS holds TLS specific configurations type KvTLS struct { - CA string - Cert string - Key string - InsecureSkipVerify bool + CA string `description:"TLS CA"` + Cert string `description:"TLS cert"` + Key string `description:"TLS key"` + InsecureSkipVerify bool `description:"TLS insecure skip verify"` } func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error { diff --git a/provider/marathon.go b/provider/marathon.go index 63ba9d8c0..91b4a2e8d 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -20,10 +20,10 @@ import ( // Marathon holds configuration of the Marathon provider. type Marathon struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - Domain string - ExposedByDefault bool + BaseProvider `mapstructure:",squash" description:"go through"` + Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"` + Domain string `description:"Default domain used"` + ExposedByDefault bool `description:"Expose Marathon apps by default"` Basic *MarathonBasic TLS *tls.Config marathonClient marathon.Marathon @@ -36,8 +36,8 @@ type MarathonBasic struct { } type lightMarathonClient interface { - Applications(url.Values) (*marathon.Applications, error) AllTasks(v url.Values) (*marathon.Tasks, error) + Applications(url.Values) (*marathon.Applications, error) } // Provide allows the provider to provide configurations to traefik diff --git a/provider/provider.go b/provider/provider.go index 3fa7612ec..eddf5a76c 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -22,8 +22,8 @@ type Provider interface { // BaseProvider should be inherited by providers type BaseProvider struct { - Watch bool - Filename string + Watch bool `description:"Watch provider"` + Filename string `description:"Override default configuration template. For advanced users :)"` } func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) { diff --git a/provider/zk.go b/provider/zk.go index 77b28100f..e8164a75f 100644 --- a/provider/zk.go +++ b/provider/zk.go @@ -9,7 +9,7 @@ import ( // Zookepper holds configurations of the Zookepper provider. type Zookepper struct { - Kv + Kv `description:"go through"` } // Provide allows the provider to provide configurations to traefik diff --git a/traefik.go b/traefik.go index dec978a6f..a1c060914 100644 --- a/traefik.go +++ b/traefik.go @@ -1,16 +1,111 @@ package main import ( + "encoding/json" + log "github.com/Sirupsen/logrus" + "github.com/containous/flaeg" + "github.com/containous/staert" + "github.com/containous/traefik/middlewares" fmtlog "log" + "net/http" "os" + "reflect" "runtime" + "strings" ) func main() { runtime.GOMAXPROCS(runtime.NumCPU()) - if err := traefikCmd.Execute(); err != nil { + + //traefik config inits + traefikConfiguration := NewTraefikConfiguration() + traefikPointersConfiguration := NewTraefikPointersConfiguration() + //traefik Command init + traefikCmd := &flaeg.Command{ + Name: "traefik", + Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. +Complete documentation is available at https://traefik.io`, + Config: traefikConfiguration, + DefaultPointersConfig: traefikPointersConfiguration, + Run: func() error { + run(traefikConfiguration) + return nil + }, + } + + //version Command init + versionCmd := &flaeg.Command{ + Name: "version", + Description: `Print version`, + Run: func() error { + fmtlog.Println(Version + " built on the " + BuildDate) + return nil + }, + } + + //staert init + s := staert.NewStaert(traefikCmd) + + //init toml source + toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."}) + //init flaeg source + f := flaeg.New(traefikCmd, os.Args[1:]) + //add custom parsers + f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{}) + f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{}) + //Wait for DefaultSliceStringParser + //add version command + f.AddCommand(versionCmd) + + //add sources to staert + s.AddSource(f) + s.AddSource(toml) + s.AddSource(f) + if err := s.Run(); err != nil { fmtlog.Println(err) os.Exit(-1) } + os.Exit(0) } + +func run(traefikConfiguration *TraefikConfiguration) { + fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) + + // load global configuration + globalConfiguration := traefikConfiguration.GlobalConfiguration + + http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost + loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile) + defer loggerMiddleware.Close() + + // logging + level, err := log.ParseLevel(strings.ToLower(globalConfiguration.LogLevel)) + if err != nil { + log.Fatal("Error getting level", err) + } + log.SetLevel(level) + + if len(globalConfiguration.TraefikLogsFile) > 0 { + fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + defer func() { + if err := fi.Close(); err != nil { + log.Error("Error closinf file", err) + } + }() + if err != nil { + log.Fatal("Error opening file", err) + } else { + log.SetOutput(fi) + log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true}) + } + } else { + log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true}) + } + jsonConf, _ := json.Marshal(globalConfiguration) + log.Debugf("Global configuration loaded %s", string(jsonConf)) + server := NewServer(globalConfiguration) + server.Start() + defer server.Close() + log.Info("Shutting down") +} diff --git a/web.go b/web.go index f32874aa6..bf77685a4 100644 --- a/web.go +++ b/web.go @@ -23,10 +23,11 @@ var metrics = stats.New() // WebProvider is a provider.Provider implementation that provides the UI. // FIXME to be handled another way. type WebProvider struct { - Address string - CertFile, KeyFile string - ReadOnly bool - server *Server + Address string `description:"Web administration port"` + CertFile string `description:"SSL certificate"` + KeyFile string `description:"SSL certificate"` + ReadOnly bool `description:"Enable read only API"` + server *Server } var ( From 414fb1f406951f17226fda57172c574cd55dc4e1 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 18 May 2016 15:45:48 +0200 Subject: [PATCH 05/19] add kubernetes.Namespaces parser --- provider/kubernetes.go | 24 ++++++++++++++++++++++++ traefik.go | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/provider/kubernetes.go b/provider/kubernetes.go index bdaf245ce..3dd3e9e4e 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -1,6 +1,7 @@ package provider import ( + "fmt" log "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" "github.com/containous/traefik/provider/k8s" @@ -23,6 +24,29 @@ const ( // Namespaces holds kubernetes namespaces type Namespaces []string +//Set adds strings elem into the the parser +//it splits str on , and ; +func (ns *Namespaces) Set(str string) error { + fargs := func(c rune) bool { + return c == ',' || c == ';' + } + // get function + slice := strings.FieldsFunc(str, fargs) + *ns = append(*ns, slice...) + return nil +} + +//Get []string +func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) } + +//String return slice in a string +func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) } + +//SetValue sets []string into the parser +func (ns *Namespaces) SetValue(val interface{}) { + *ns = Namespaces(val.(Namespaces)) +} + // Kubernetes holds configurations of the Kubernetes provider. type Kubernetes struct { BaseProvider `mapstructure:",squash"` diff --git a/traefik.go b/traefik.go index a1c060914..b0e2cb745 100644 --- a/traefik.go +++ b/traefik.go @@ -6,6 +6,7 @@ import ( "github.com/containous/flaeg" "github.com/containous/staert" "github.com/containous/traefik/middlewares" + "github.com/containous/traefik/provider" fmtlog "log" "net/http" "os" @@ -53,7 +54,7 @@ Complete documentation is available at https://traefik.io`, //add custom parsers f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{}) f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{}) - //Wait for DefaultSliceStringParser + f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{}) //add version command f.AddCommand(versionCmd) From e115e3c4e70b4f5a0aa83a35303450c17508d277 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 19 May 2016 17:12:36 +0200 Subject: [PATCH 06/19] fix default value --- configuration.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration.go b/configuration.go index 9b39d5db6..e54e49c90 100644 --- a/configuration.go +++ b/configuration.go @@ -292,8 +292,8 @@ func NewTraefikConfiguration() *TraefikConfiguration { return &TraefikConfiguration{ GlobalConfiguration: GlobalConfiguration{ GraceTimeOut: 10, - AccessLogsFile: "log/access.log", - TraefikLogsFile: "log/traefik.log", + AccessLogsFile: "", + TraefikLogsFile: "", LogLevel: "ERROR", EntryPoints: map[string]*EntryPoint{"http": &EntryPoint{Address: ":80"}}, DefaultEntryPoints: []string{"http"}, From 629be45c4a779ea7553a80abacdeb6a31b5682c1 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 19 May 2016 17:44:33 +0200 Subject: [PATCH 07/19] fix DisablePassHostHeaders --- provider/kubernetes_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index 2e31a7df9..8426b73ad 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -416,7 +416,7 @@ func TestRuleType(t *testing.T) { services: services, watchChan: watchChan, } - provider := Kubernetes{disablePassHostHeaders: true} + provider := Kubernetes{DisablePassHostHeaders: true} actualConfig, err := provider.loadIngresses(client) actual := actualConfig.Frontends if err != nil { @@ -538,7 +538,7 @@ func TestGetPassHostHeader(t *testing.T) { services: services, watchChan: watchChan, } - provider := Kubernetes{disablePassHostHeaders: true} + provider := Kubernetes{DisablePassHostHeaders: true} actual, err := provider.loadIngresses(client) if err != nil { t.Fatalf("error %+v", err) From 1e27c2dabe9cbaa63493b86559d43f8ada19aebf Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 14:23:42 +0200 Subject: [PATCH 08/19] fix TestNoOrInexistentConfigShouldNotFail --- integration/basic_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration/basic_test.go b/integration/basic_test.go index 40f5ab997..0e7220279 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -15,7 +15,7 @@ import ( // SimpleSuite type SimpleSuite struct{ BaseSuite } -func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) { +func (s *SimpleSuite) TestNoOrInexistentConfigShouldNotFail(c *check.C) { cmd := exec.Command(traefikBinary) var b bytes.Buffer @@ -26,7 +26,7 @@ func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) { time.Sleep(500 * time.Millisecond) output := b.Bytes() - c.Assert(string(output), checker.Contains, "No configuration file found") + c.Assert(string(output), checker.Not(checker.Contains), "No configuration file found") cmd.Process.Kill() nonExistentFile := "non/existent/file.toml" @@ -39,7 +39,7 @@ func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) { time.Sleep(500 * time.Millisecond) output = b.Bytes() - c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading configuration file: open %s: no such file or directory", nonExistentFile)) + c.Assert(string(output), checker.Not(checker.Contains), fmt.Sprintf("Error reading configuration file: open %s: no such file or directory", nonExistentFile)) cmd.Process.Kill() } @@ -55,7 +55,7 @@ func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { defer cmd.Process.Kill() output := b.Bytes() - c.Assert(string(output), checker.Contains, "While parsing config: Near line 0 (last key parsed ''): Bare keys cannot contain '{'") + c.Assert(string(output), checker.Contains, "Near line 0 (last key parsed ''): Bare keys cannot contain '{'") } func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) { From 1a0f347023b32dc687b1206ec08e50ce6a6ae8e2 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 14:58:25 +0200 Subject: [PATCH 09/19] update default value --- configuration.go | 6 +++--- traefik.go | 33 +++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/configuration.go b/configuration.go index e54e49c90..daa037246 100644 --- a/configuration.go +++ b/configuration.go @@ -77,7 +77,7 @@ func (dep *DefaultEntryPoints) SetValue(val interface{}) { // Type is type of the struct func (dep *DefaultEntryPoints) Type() string { - return fmt.Sprint("defaultentrypoints²") + return fmt.Sprint("defaultentrypoints") } // EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...) @@ -295,8 +295,8 @@ func NewTraefikConfiguration() *TraefikConfiguration { AccessLogsFile: "", TraefikLogsFile: "", LogLevel: "ERROR", - EntryPoints: map[string]*EntryPoint{"http": &EntryPoint{Address: ":80"}}, - DefaultEntryPoints: []string{"http"}, + EntryPoints: map[string]*EntryPoint{}, + DefaultEntryPoints: []string{}, ProvidersThrottleDuration: time.Duration(2 * time.Second), MaxIdleConnsPerHost: 200, }, diff --git a/traefik.go b/traefik.go index b0e2cb745..adb5c4475 100644 --- a/traefik.go +++ b/traefik.go @@ -36,19 +36,16 @@ Complete documentation is available at https://traefik.io`, //version Command init versionCmd := &flaeg.Command{ - Name: "version", - Description: `Print version`, + Name: "version", + Description: `Print version`, + Config: struct{}{}, + DefaultPointersConfig: struct{}{}, Run: func() error { fmtlog.Println(Version + " built on the " + BuildDate) return nil }, } - //staert init - s := staert.NewStaert(traefikCmd) - - //init toml source - toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."}) //init flaeg source f := flaeg.New(traefikCmd, os.Args[1:]) //add custom parsers @@ -57,11 +54,31 @@ Complete documentation is available at https://traefik.io`, f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{}) //add version command f.AddCommand(versionCmd) + if _, err := f.Parse(traefikCmd); err != nil { + fmtlog.Println(err) + os.Exit(-1) + } + + //staert init + s := staert.NewStaert(traefikCmd) + //init toml source + toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."}) //add sources to staert - s.AddSource(f) s.AddSource(toml) s.AddSource(f) + if _, err := s.GetConfig(); err != nil { + fmtlog.Println(err) + } + if traefikConfiguration.File != nil && len(traefikConfiguration.File.Filename) == 0 { + // no filename, setting to global config file + log.Debugf("ConfigFileUsed %s", toml.ConfigFileUsed()) + traefikConfiguration.File.Filename = toml.ConfigFileUsed() + } + if len(traefikConfiguration.EntryPoints) == 0 { + traefikConfiguration.EntryPoints = map[string]*EntryPoint{"http": &EntryPoint{Address: ":80"}} + traefikConfiguration.DefaultEntryPoints = []string{"http"} + } if err := s.Run(); err != nil { fmtlog.Println(err) os.Exit(-1) From 89e00eb5a452f0a4e2e7f5767a3e0fc0e6f157c8 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 14:59:18 +0200 Subject: [PATCH 10/19] add staert & fleag --- glide.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/glide.yaml b/glide.yaml index 3ca22fb8c..7d9d76ba8 100644 --- a/glide.yaml +++ b/glide.yaml @@ -187,3 +187,6 @@ import: - package: github.com/parnurzeal/gorequest - package: github.com/mattn/go-shellwords - package: github.com/moul/http2curl +- package: github.com/containous/flaeg +- package: github.com/containous/staert +- package: github.com/ogier/pflag From 0821c7bdd9c72521a756184142cbd098e070227d Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 15:45:20 +0200 Subject: [PATCH 11/19] Add version in logs --- traefik.go | 1 + 1 file changed, 1 insertion(+) diff --git a/traefik.go b/traefik.go index adb5c4475..636514f2d 100644 --- a/traefik.go +++ b/traefik.go @@ -121,6 +121,7 @@ func run(traefikConfiguration *TraefikConfiguration) { log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true}) } jsonConf, _ := json.Marshal(globalConfiguration) + log.Infof("Traefik version %s built on %s", Version, BuildDate) log.Debugf("Global configuration loaded %s", string(jsonConf)) server := NewServer(globalConfiguration) server.Start() From 059da90a96565cdbd506b5b4c2dcbf4ff6806e50 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 15:45:41 +0200 Subject: [PATCH 12/19] clean glide dependancies --- glide.lock | 172 +++++++++++----------------------------- glide.yaml | 228 +++++++++++++---------------------------------------- 2 files changed, 103 insertions(+), 297 deletions(-) diff --git a/glide.lock b/glide.lock index b52394f42..a88e1030c 100644 --- a/glide.lock +++ b/glide.lock @@ -1,39 +1,40 @@ -hash: 68bc4f87206f9a486e1455f1dfcad737369c359a803566271432fcb85de3a12c -updated: 2016-05-23T13:57:35.191541555+02:00 +hash: 50e873651126f0af9204fd8cc11f10917ce56b76479310ada578f5487162e0c4 +updated: 2016-05-24T15:42:45.810673781+02:00 imports: -- name: github.com/alecthomas/template - version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0 -- name: github.com/alecthomas/units - version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915 - name: github.com/boltdb/bolt - version: 51f99c862475898df9773747d3accd05a7ca33c1 + version: dfb21201d9270c1082d5fb0f07f500311ff72f18 - name: github.com/BurntSushi/toml - version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f + version: f0aeabca5a127c4078abb8c8d64298b147264b55 - name: github.com/BurntSushi/ty version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 subpackages: - fun - name: github.com/cenkalti/backoff - version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99 + version: c29158af31815ccc31ca29c86c121bc39e00d3d8 - name: github.com/codahale/hdrhistogram - version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17 + version: 9208b142303c12d8899bae836fd524ac9338b4fd - name: github.com/codegangsta/cli version: bf4a526f48af7badd25d2cb02d587e1b01be3b50 - name: github.com/codegangsta/negroni - version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b + version: ffbc66b612ee3eac2eba29aedce4c3a65e4dd0a1 +- name: github.com/containous/flaeg + version: e390ea90e4ee21ff73fb1630b8c50a843a6d7674 - name: github.com/containous/oxy version: 183212964e13e7b8afe01a08b193d04300554a68 subpackages: - cbreaker - - forward - - memmetrics - - roundrobin - - utils - connlimit + - forward + - roundrobin - stream + - utils +- name: github.com/containous/staert + version: f0da5ea8404f0d3999d72ba8f8736d3aa4b8c8f1 - name: github.com/coreos/etcd - version: 26e52d2bce9e3e11b77b68cc84bf91aebb1ef637 + version: c400d05d0aa73e21e431c16145e558d624098018 subpackages: + - Godeps/_workspace/src/github.com/ugorji/go/codec + - Godeps/_workspace/src/golang.org/x/net/context - client - pkg/pathutil - pkg/types @@ -42,72 +43,37 @@ imports: subpackages: - spew - name: github.com/docker/distribution - version: 467fc068d88aa6610691b7f1a677271a3fac4aac + version: 5bbf65499960b184fe8e0f045397375e1a6722b8 subpackages: - reference - digest - name: github.com/docker/docker version: 9837ec4da53f15f9120d53a6e1517491ba8b0261 subpackages: - - autogen - - api - - cliconfig - - daemon/network - - graph/tags - - image - - opts - - pkg/archive - - pkg/fileutils - - pkg/homedir - - pkg/httputils - - pkg/ioutils - - pkg/jsonmessage - - pkg/mflag - - pkg/nat - - pkg/parsers - - pkg/pools - - pkg/promise - - pkg/random - - pkg/stdcopy - - pkg/stringid - - pkg/symlink - - pkg/system - - pkg/tarsum - - pkg/term - - pkg/timeutils - - pkg/tlsconfig - - pkg/ulimit - - pkg/units - - pkg/urlutil - - pkg/useragent - - pkg/version - - registry - - runconfig - - utils - - volume + - namesgenerator - name: github.com/docker/engine-api version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: - client - types - - types/container - - types/filters - - types/strslice - types/events + - types/filters - client/transport - client/transport/cancellable + - types/container - types/network - types/reference - types/registry - types/time - types/versions - types/blkiodev + - types/strslice - name: github.com/docker/go-connections version: c7838b258fbfa3fe88eecfb2a0e08ea0dbd6a646 subpackages: - - nat - sockets - tlsconfig + - nat - name: github.com/docker/go-units version: 5d2041e26a699eaca682e2ea41c8f891e1060444 - name: github.com/docker/libcompose @@ -120,61 +86,39 @@ imports: - store/consul - store/etcd - store/zookeeper -- name: github.com/docker/libtrust - version: 9cbd2a1374f46905c68a4eb3694a130610adc62a - name: github.com/donovanhide/eventsource - version: d8a3071799b98cacd30b6da92f536050ccfe6da4 + version: c3f57f280ec708df24886d9e62f2fd178d69d8e8 - name: github.com/elazarl/go-bindata-assetfs - version: d5cac425555ca5cf00694df246e04f05e6a55150 -- name: github.com/flynn/go-shlex - version: 3f9db97f856818214da2e1057f8ad84803971cff + version: 57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2 - name: github.com/gambol99/go-marathon version: ade11d1dc2884ee1f387078fc28509559b6235d1 - name: github.com/go-check/check - version: 11d3bc7aa68e238947792f30573146a3231fc0f1 -- name: github.com/golang/glog - version: fca8c8854093a154ff1eb580aae10276ad6b1b5f + version: 4f90aeace3a26ad7021961c297b22c42160c7b25 - name: github.com/google/go-querystring version: 9235644dd9e52eeae6fa48efd539fdc351a0af53 subpackages: - query - name: github.com/gorilla/context - version: 215affda49addc4c8ef7e2534915df2c8c35c6cd -- name: github.com/gorilla/handlers - version: 40694b40f4a928c062f56849989d3e9cd0570e5f + version: a8d44e7d8e4d532b6a27a02dd82abb31cc1b01bd - name: github.com/gorilla/mux - version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a -- name: github.com/gorilla/websocket - version: 1f512fc3f05332ba7117626cdfb4e07474e58e60 + version: 9c19ed558d5df4da88e2ade9c8940d742aef0e7e - name: github.com/hashicorp/consul - version: de080672fee9e6104572eeea89eccdca135bb918 + version: f6fef66e1bf17be4f3c9855fbec6de802ca6bd7d subpackages: - api -- name: github.com/hashicorp/hcl - version: 9a905a34e6280ce905da1a32344b25e81011197a +- name: github.com/hashicorp/go-cleanhttp + version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d +- name: github.com/hashicorp/serf + version: e4ec8cc423bbe20d26584b96efbeb9102e16d05f subpackages: - - hcl/ast - - hcl/parser - - hcl/token - - json/parser - - hcl/scanner - - hcl/strconv - - json/scanner - - json/token -- name: github.com/inconshreveable/mousetrap - version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -- name: github.com/kr/pretty - version: add1dbc86daf0f983cd4a48ceb39deb95c729b67 -- name: github.com/kr/text - version: 7cafcd837844e784b526369c9bce262804aebc60 + - coordinate + - serf - name: github.com/libkermit/docker version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 - name: github.com/libkermit/docker-check version: bb75a86b169c6c5d22c0ee98278124036f272d7b -- name: github.com/magiconair/properties - version: c265cfa48dda6474e208715ca93e987829f572f8 - name: github.com/mailgun/log - version: 44874009257d4d47ba9806f1b7f72a32a015e4d8 + version: 2f35a4607f1abf71f97f77f99b0de8493ef6f4ef - name: github.com/mailgun/manners version: fada45142db3f93097ca917da107aa3fad0ffcb5 - name: github.com/mailgun/multibuf @@ -187,12 +131,12 @@ imports: version: 4f1a71750d95a5a8a46c40a67ffbed8129c2f138 - name: github.com/miekg/dns version: 48ab6605c66ac797e07f615101c3e9e10e932b66 -- name: github.com/mitchellh/mapstructure - version: d2dd0262208475919e1a362f675cfc0e7c10e905 - name: github.com/moul/http2curl version: b1479103caacaa39319f75e7f57fc545287fca0d +- name: github.com/ogier/pflag + version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc - version: 2441732d6fcc0fb0a542671a4372e0c7bc99c19e + version: d2d09b9bcd0573c58d7cd94e57bd7555af0c2072 subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest @@ -202,25 +146,13 @@ imports: subpackages: - difflib - name: github.com/samuel/go-zookeeper - version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b + version: 6eb1b09c6ce23f305f4c81bf748b22fbc6f3f9e9 subpackages: - zk - name: github.com/Sirupsen/logrus - version: 418b41d23a1bf978c06faea5313ba194650ac088 -- name: github.com/spf13/cast - version: ee7b3e0353166ab1f3a605294ac8cd2b77953778 -- name: github.com/spf13/cobra - version: f368244301305f414206f889b1735a54cfc8bde8 - subpackages: - - cobra -- name: github.com/spf13/jwalterweatherman - version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 -- name: github.com/spf13/pflag - version: cb88ea77998c3f024757528e3305022ab50b43be -- name: github.com/spf13/viper - version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325 + version: 6d9ae300aaf85d6acd2e5424081c7fcddb21dab8 - name: github.com/streamrail/concurrent-map - version: 1ce4642e5a162df67825d273a86b87e6cc8a076b + version: 65a174a3a4188c0b7099acbc6cfa0c53628d3287 - name: github.com/stretchr/objx version: cbeaeb16a013161a98496fad62933b1d21786672 - name: github.com/stretchr/testify @@ -229,13 +161,9 @@ imports: - mock - assert - name: github.com/thoas/stats - version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8 -- name: github.com/ugorji/go - version: ea9cd21fa0bc41ee4bdd50ac7ed8cbc7ea2ed960 - subpackages: - - codec + version: 69e3c072eec2df2df41afe6214f62eb940e4cd80 - name: github.com/unrolled/render - version: 26b4e3aac686940fe29521545afad9966ddfc80c + version: 198ad4d8b8a4612176b804ca10555b222a086b40 - name: github.com/vdemeester/docker-events version: b308d2e8d639d928c882913bcb4f85b3a84c7a07 - name: github.com/vdemeester/shakers @@ -255,8 +183,6 @@ imports: - plugin/rewrite - plugin - router -- name: github.com/wendal/errors - version: f66c77a7882b399795a8987ebf87ef64a427417e - name: github.com/xenolf/lego version: b119bc45fbd1cc71348003541aac9d3a7da63654 subpackages: @@ -266,7 +192,7 @@ imports: subpackages: - ocsp - name: golang.org/x/net - version: d9558e5c97f85372afee28cf2b6059d7d3818919 + version: 6460565bec1e8891e29ff478184c71b9e443ac36 subpackages: - context - publicsuffix @@ -276,12 +202,10 @@ imports: subpackages: - unix - windows -- name: gopkg.in/alecthomas/kingpin.v2 - version: 639879d6110b1b0409410c7b737ef0bb18325038 - name: gopkg.in/fsnotify.v1 - version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0 + version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 - name: gopkg.in/mgo.v2 - version: 22287bab4379e1fbf6002fb4eb769888f3fb224c + version: b6e2fa371e64216a45e61072a96d4e3859f169da subpackages: - bson - name: gopkg.in/square/go-jose.v1 @@ -289,6 +213,4 @@ imports: subpackages: - cipher - json -- name: gopkg.in/yaml.v2 - version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 devImports: [] diff --git a/glide.yaml b/glide.yaml index 7d9d76ba8..df8f8dcf5 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,192 +1,76 @@ -package: main +package: github.com/containous/traefik import: -- package: github.com/coreos/etcd - version: 26e52d2bce9e3e11b77b68cc84bf91aebb1ef637 +- package: github.com/BurntSushi/toml +- package: github.com/BurntSushi/ty subpackages: - - client -- package: github.com/mailgun/log - version: 44874009257d4d47ba9806f1b7f72a32a015e4d8 + - fun +- package: github.com/Sirupsen/logrus +- package: github.com/cenkalti/backoff +- package: github.com/codegangsta/negroni +- package: github.com/containous/flaeg - package: github.com/containous/oxy - version: 183212964e13e7b8afe01a08b193d04300554a68 subpackages: - cbreaker + - connlimit - forward - - memmetrics - roundrobin + - stream - utils -- package: github.com/hashicorp/consul - version: de080672fee9e6104572eeea89eccdca135bb918 - subpackages: - - api -- package: github.com/samuel/go-zookeeper - version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b - subpackages: - - zk -- package: github.com/docker/libtrust - version: 9cbd2a1374f46905c68a4eb3694a130610adc62a -- package: github.com/go-check/check - version: 11d3bc7aa68e238947792f30573146a3231fc0f1 -- package: golang.org/x/net - version: d9558e5c97f85372afee28cf2b6059d7d3818919 - subpackages: - - context -- package: github.com/gorilla/handlers - version: 40694b40f4a928c062f56849989d3e9cd0570e5f -- package: github.com/docker/libkv - version: 7283ef27ed32fe267388510a91709b307bb9942c -- package: github.com/alecthomas/template - version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0 -- package: github.com/vdemeester/shakers - version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 -- package: github.com/alecthomas/units - version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915 -- package: github.com/gambol99/go-marathon - version: ade11d1dc2884ee1f387078fc28509559b6235d1 -- package: github.com/vulcand/predicate - version: cb0bff91a7ab7cf7571e661ff883fc997bc554a3 -- package: github.com/thoas/stats - version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8 -- package: github.com/Sirupsen/logrus - version: 418b41d23a1bf978c06faea5313ba194650ac088 -- package: github.com/unrolled/render - version: 26b4e3aac686940fe29521545afad9966ddfc80c -- package: github.com/flynn/go-shlex - version: 3f9db97f856818214da2e1057f8ad84803971cff -- package: github.com/boltdb/bolt - version: 51f99c862475898df9773747d3accd05a7ca33c1 -- package: gopkg.in/mgo.v2 - version: 22287bab4379e1fbf6002fb4eb769888f3fb224c - subpackages: - - bson -- package: github.com/docker/docker - version: 9837ec4da53f15f9120d53a6e1517491ba8b0261 - subpackages: - - autogen - - api - - cliconfig - - daemon/network - - graph/tags - - image - - opts - - pkg/archive - - pkg/fileutils - - pkg/homedir - - pkg/httputils - - pkg/ioutils - - pkg/jsonmessage - - pkg/mflag - - pkg/nat - - pkg/parsers - - pkg/pools - - pkg/promise - - pkg/random - - pkg/stdcopy - - pkg/stringid - - pkg/symlink - - pkg/system - - pkg/tarsum - - pkg/term - - pkg/timeutils - - pkg/tlsconfig - - pkg/ulimit - - pkg/units - - pkg/urlutil - - pkg/useragent - - pkg/version - - registry - - runconfig - - utils - - volume -- package: github.com/mailgun/timetools - version: fd192d755b00c968d312d23f521eb0cdc6f66bd0 -- package: github.com/codegangsta/negroni - version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b -- package: gopkg.in/yaml.v2 - version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf -- package: github.com/opencontainers/runc - version: 2441732d6fcc0fb0a542671a4372e0c7bc99c19e - subpackages: - - libcontainer/user -- package: github.com/gorilla/mux - version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a -- package: github.com/BurntSushi/ty - version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 -- package: github.com/elazarl/go-bindata-assetfs - version: d5cac425555ca5cf00694df246e04f05e6a55150 -- package: github.com/BurntSushi/toml - version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f -- package: gopkg.in/alecthomas/kingpin.v2 - version: 639879d6110b1b0409410c7b737ef0bb18325038 -- package: github.com/cenkalti/backoff - version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99 -- package: gopkg.in/fsnotify.v1 - version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0 -- package: github.com/mailgun/manners - version: fada45142db3f93097ca917da107aa3fad0ffcb5 -- package: github.com/gorilla/context - version: 215affda49addc4c8ef7e2534915df2c8c35c6cd -- package: github.com/codahale/hdrhistogram - version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17 -- package: github.com/gorilla/websocket -- package: github.com/donovanhide/eventsource - version: d8a3071799b98cacd30b6da92f536050ccfe6da4 -- package: github.com/golang/glog - version: fca8c8854093a154ff1eb580aae10276ad6b1b5f -- package: github.com/spf13/cast - version: ee7b3e0353166ab1f3a605294ac8cd2b77953778 -- package: github.com/mitchellh/mapstructure -- package: github.com/spf13/jwalterweatherman -- package: github.com/spf13/pflag -- package: github.com/wendal/errors -- package: github.com/hashicorp/hcl -- package: github.com/kr/pretty -- package: github.com/magiconair/properties -- package: github.com/kr/text -- package: github.com/spf13/viper - version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325 -- package: github.com/spf13/cobra - subpackages: - - cobra -- package: github.com/google/go-querystring - subpackages: - - query -- package: github.com/vulcand/vulcand - subpackages: - - plugin/rewrite -- package: github.com/stretchr/testify - subpackages: - - mock -- package: github.com/xenolf/lego -- package: github.com/libkermit/docker-check - version: bb75a86b169c6c5d22c0ee98278124036f272d7b -- package: github.com/libkermit/docker - version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 -- package: github.com/docker/libcompose - version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873 -- package: github.com/docker/distribution - version: 467fc068d88aa6610691b7f1a677271a3fac4aac - subpackages: - - reference +- package: github.com/containous/staert - package: github.com/docker/engine-api version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: - client - types - - types/container + - types/events - types/filters - - types/strslice -- package: github.com/vdemeester/docker-events - package: github.com/docker/go-connections subpackages: - - nat - sockets - tlsconfig -- package: github.com/docker/go-units -- package: github.com/mailgun/multibuf -- package: github.com/streamrail/concurrent-map +- package: github.com/docker/libkv + subpackages: + - store + - store/boltdb + - store/consul + - store/etcd + - store/zookeeper +- package: github.com/elazarl/go-bindata-assetfs +- package: github.com/gambol99/go-marathon + version: ade11d1dc2884ee1f387078fc28509559b6235d1 +- package: github.com/gorilla/mux +- package: github.com/hashicorp/consul + subpackages: + - api +- package: github.com/mailgun/manners - package: github.com/parnurzeal/gorequest +- package: github.com/streamrail/concurrent-map +- package: github.com/stretchr/testify + subpackages: + - mock +- package: github.com/thoas/stats +- package: github.com/unrolled/render +- package: github.com/vdemeester/docker-events +- package: github.com/vulcand/vulcand + subpackages: + - plugin/rewrite +- package: github.com/xenolf/lego + subpackages: + - acme +- package: golang.org/x/net + subpackages: + - context +- package: gopkg.in/fsnotify.v1 +- package: github.com/libkermit/docker-check + version: bb75a86b169c6c5d22c0ee98278124036f272d7b +- package: github.com/libkermit/docker + version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 +- package: github.com/docker/docker + version: 9837ec4da53f15f9120d53a6e1517491ba8b0261 + subpackages: + - namesgenerator +- package: github.com/go-check/check +- package: github.com/docker/libcompose + version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873 - package: github.com/mattn/go-shellwords -- package: github.com/moul/http2curl -- package: github.com/containous/flaeg -- package: github.com/containous/staert -- package: github.com/ogier/pflag +- package: github.com/vdemeester/shakers From ab138e7df12f4c632bc587e2e192f8d5f9d0bbe9 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 17:29:56 +0200 Subject: [PATCH 13/19] update to new version go-bindata-assetfs --- web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web.go b/web.go index bf77685a4..f402edfec 100644 --- a/web.go +++ b/web.go @@ -93,7 +93,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) { http.Redirect(response, request, "/dashboard/", 302) }) - systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetDir: autogen.AssetDir, Prefix: "static"}))) + systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetInfo: autogen.AssetInfo, AssetDir: autogen.AssetDir, Prefix: "static"}))) // expvars if provider.server.globalConfiguration.Debug { From 6752b495369763aee445f22e63c786e447456dbd Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 24 May 2016 17:31:50 +0200 Subject: [PATCH 14/19] rm useless StrucTag --- flaeg_test.go | 91 -------------------------------------- provider/boltdb.go | 2 +- provider/consul.go | 2 +- provider/consul_catalog.go | 10 ++--- provider/docker.go | 8 ++-- provider/etcd.go | 2 +- provider/file.go | 2 +- provider/kubernetes.go | 2 +- provider/kv.go | 12 ++--- traefik.go | 2 +- 10 files changed, 21 insertions(+), 112 deletions(-) delete mode 100644 flaeg_test.go diff --git a/flaeg_test.go b/flaeg_test.go deleted file mode 100644 index bbf36c42d..000000000 --- a/flaeg_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "github.com/cocap10/flaeg" - "reflect" - "testing" - "time" -) - -func TestLoad(t *testing.T) { - var configuration GlobalConfiguration - defaultConfiguration := NewGlobalConfiguration() - args := []string{ - // "-h", - "--docker", - "--file", - "--web", - "--marathon", - "--consul", - "--consulcatalog", - "--etcd", - "--zookeeper", - "--boltdb", - } - if err := flaeg.Load(&configuration, defaultConfiguration, args); err != nil { - t.Fatalf("Error: %s", err) - } - // fmt.Printf("result : \n%+v\n", configuration) - if !reflect.DeepEqual(configuration, *defaultConfiguration) { - t.Fatalf("\nexpected\t: %+v\ngot\t\t\t: %+v", *defaultConfiguration, configuration) - } -} - -func TestLoadWithParsers(t *testing.T) { - var configuration GlobalConfiguration - defaultConfiguration := NewGlobalConfiguration() - args := []string{ - // "-h", - "--docker", - // "--file", - "--web.address=:8888", - "--marathon", - "--consul", - "--consulcatalog", - "--etcd.tls.insecureskipverify", - "--zookeeper", - "--boltdb", - "--accesslogsfile=log2/access.log", - "--entrypoints=Name:http Address::8000 Redirect.EntryPoint:https", - "--entrypoints=Name:https Address::8443 Redirect.EntryPoint:http", - "--defaultentrypoints=https", - "--defaultentrypoints=ssh", - "--providersthrottleduration=4s", - } - parsers := map[reflect.Type]flaeg.Parser{} - var defaultEntryPointsParser DefaultEntryPoints - parsers[reflect.TypeOf(DefaultEntryPoints{})] = &defaultEntryPointsParser - entryPointsParser := EntryPoints{} - parsers[reflect.TypeOf(EntryPoints{})] = &entryPointsParser - - if err := flaeg.LoadWithParsers(&configuration, defaultConfiguration, args, parsers); err != nil { - t.Fatalf("Error: %s", err) - } - // fmt.Printf("result : \n%+v\n", configuration) - - //Check - check := *defaultConfiguration - check.File = nil - check.Web.Address = ":8888" - check.AccessLogsFile = "log2/access.log" - check.Etcd.TLS.InsecureSkipVerify = true - check.EntryPoints = make(map[string]*EntryPoint) - check.EntryPoints["http"] = &EntryPoint{ - Address: ":8000", - Redirect: &Redirect{ - EntryPoint: "https", - }, - } - check.EntryPoints["https"] = &EntryPoint{ - Address: ":8443", - Redirect: &Redirect{ - EntryPoint: "http", - }, - } - check.DefaultEntryPoints = []string{"https", "ssh"} - check.ProvidersThrottleDuration = time.Duration(4 * time.Second) - - if !reflect.DeepEqual(&configuration, &check) { - t.Fatalf("\nexpected\t: %+v\ngot\t\t\t: %+v", check, configuration) - } -} diff --git a/provider/boltdb.go b/provider/boltdb.go index 966a4b8da..4c2a33844 100644 --- a/provider/boltdb.go +++ b/provider/boltdb.go @@ -9,7 +9,7 @@ import ( // BoltDb holds configurations of the BoltDb provider. type BoltDb struct { - Kv `mapstructure:",squash" description:"go through"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/provider/consul.go b/provider/consul.go index d6d81f3e7..d94dc7e03 100644 --- a/provider/consul.go +++ b/provider/consul.go @@ -9,7 +9,7 @@ import ( // Consul holds configurations of the Consul provider. type Consul struct { - Kv `mapstructure:",squash" description:"go through"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/provider/consul_catalog.go b/provider/consul_catalog.go index c17cfe0a9..2aecc4622 100644 --- a/provider/consul_catalog.go +++ b/provider/consul_catalog.go @@ -23,11 +23,11 @@ const ( // ConsulCatalog holds configurations of the Consul catalog provider. type ConsulCatalog struct { - BaseProvider `mapstructure:",squash"` - Endpoint string `description:"Consul server endpoint"` - Domain string `description:"Default domain used"` - client *api.Client - Prefix string + BaseProvider + Endpoint string `description:"Consul server endpoint"` + Domain string `description:"Default domain used"` + client *api.Client + Prefix string } type serviceUpdate struct { diff --git a/provider/docker.go b/provider/docker.go index 55b73f8b6..75f5c4ef8 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -29,10 +29,10 @@ const DockerAPIVersion string = "1.21" // Docker holds configurations of the Docker provider. type Docker struct { - BaseProvider `mapstructure:",squash"` - Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` - Domain string `description:"Default domain used"` - TLS *DockerTLS `description:"Enable Docker TLS support"` + BaseProvider + Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` + Domain string `description:"Default domain used"` + TLS *DockerTLS `description:"Enable Docker TLS support"` } // DockerTLS holds TLS specific configurations diff --git a/provider/etcd.go b/provider/etcd.go index 3344245e4..a7fd7ae6a 100644 --- a/provider/etcd.go +++ b/provider/etcd.go @@ -9,7 +9,7 @@ import ( // Etcd holds configurations of the Etcd provider. type Etcd struct { - Kv `mapstructure:",squash" description:"go through"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/provider/file.go b/provider/file.go index c5597ec19..1b463593a 100644 --- a/provider/file.go +++ b/provider/file.go @@ -14,7 +14,7 @@ import ( // File holds configurations of the File provider. type File struct { - BaseProvider `mapstructure:",squash" description:"go through"` + BaseProvider } // Provide allows the provider to provide configurations to traefik diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 3dd3e9e4e..cab4217ea 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -49,7 +49,7 @@ func (ns *Namespaces) SetValue(val interface{}) { // Kubernetes holds configurations of the Kubernetes provider. type Kubernetes struct { - BaseProvider `mapstructure:",squash"` + BaseProvider Endpoint string `description:"Kubernetes server endpoint"` DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"` Namespaces Namespaces `description:"Kubernetes namespaces"` diff --git a/provider/kv.go b/provider/kv.go index 852799dcb..713416781 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -22,12 +22,12 @@ import ( // Kv holds common configurations of key-value providers. type Kv struct { - BaseProvider `mapstructure:",squash" description:"go through"` - Endpoint string `description:"Comma sepparated server endpoints"` - Prefix string `description:"Prefix used for KV store"` - TLS *KvTLS `description:"Enable TLS support"` - storeType store.Backend - kvclient store.Store + BaseProvider + Endpoint string `description:"Comma sepparated server endpoints"` + Prefix string `description:"Prefix used for KV store"` + TLS *KvTLS `description:"Enable TLS support"` + storeType store.Backend + kvclient store.Store } // KvTLS holds TLS specific configurations diff --git a/traefik.go b/traefik.go index 636514f2d..fbc6a4f7f 100644 --- a/traefik.go +++ b/traefik.go @@ -76,7 +76,7 @@ Complete documentation is available at https://traefik.io`, traefikConfiguration.File.Filename = toml.ConfigFileUsed() } if len(traefikConfiguration.EntryPoints) == 0 { - traefikConfiguration.EntryPoints = map[string]*EntryPoint{"http": &EntryPoint{Address: ":80"}} + traefikConfiguration.EntryPoints = map[string]*EntryPoint{"http": {Address: ":80"}} traefikConfiguration.DefaultEntryPoints = []string{"http"} } if err := s.Run(); err != nil { From f64c2bc065c0e306c40748ec9aceefeb3e55a7af Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 25 May 2016 17:06:34 +0200 Subject: [PATCH 15/19] add flag on ACME add flag on Retry set Retry.MaxMem to 2 by default rm useless import rm useless structtag add custom parser on []acme.Domain type add commants + refactor --- acme/acme.go | 45 +++++++++++++++++++++++++++++++++++++++------ configuration.go | 39 +++++++++++++++++++++------------------ provider/zk.go | 2 +- traefik.go | 5 ++++- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/acme/acme.go b/acme/acme.go index 53c1bd123..081e4d8e4 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -16,6 +16,7 @@ import ( fmtlog "log" "os" "reflect" + "strings" "sync" "time" ) @@ -161,15 +162,47 @@ func (dc *DomainsCertificate) needRenew() bool { // ACME allows to connect to lets encrypt and retrieve certs type ACME struct { - Email string - Domains []Domain - StorageFile string - OnDemand bool - CAServer string - EntryPoint string + Email string `description:"Email address used for registration"` + Domains []Domain `description:"SANs (alternative domains) to each main domain"` + StorageFile string `description:"File used for certificates storage."` + 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."` + CAServer string `description:"CA server to use."` + EntryPoint string `description:"Entrypoint to proxy acme challenge to."` storageLock sync.RWMutex } +//Domains parse []Domain +type Domains []Domain + +//Set []Domain +func (ds *Domains) Set(str string) error { + fargs := func(c rune) bool { + return c == ',' || c == ';' + } + // get function + slice := strings.FieldsFunc(str, fargs) + if len(slice) >= 2 { + return fmt.Errorf("Parse error ACME.Domain. Imposible to parse %s", str) + } + d := Domain{ + Main: slice[0], + SANs: slice[1:], + } + *ds = append(*ds, d) + return nil +} + +//Get []Domain +func (ds *Domains) Get() interface{} { return []Domain(*ds) } + +//String returns []Domain in string +func (ds *Domains) String() string { return fmt.Sprintf("%+v", *ds) } + +//SetValue sets []Domain into the parser +func (ds *Domains) SetValue(val interface{}) { + *ds = Domains(val.([]Domain)) +} + // Domain holds a domain name with SANs type Domain struct { Main string diff --git a/configuration.go b/configuration.go index daa037246..201a61fe8 100644 --- a/configuration.go +++ b/configuration.go @@ -20,17 +20,17 @@ type TraefikConfiguration struct { // GlobalConfiguration holds global configuration (with providers, etc.). // It's populated from the traefik configuration file passed as an argument to the binary. type GlobalConfiguration struct { - GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."` - Debug bool - AccessLogsFile string `description:"Access logs file"` - TraefikLogsFile string `description:"Traefik logs file"` - LogLevel string `short:"l" description:"Log level"` - EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'"` - ACME *acme.ACME - DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"` - ProvidersThrottleDuration time.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time."` - MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"` - Retry *Retry + GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."` + Debug bool + AccessLogsFile string `description:"Access logs file"` + TraefikLogsFile string `description:"Traefik logs file"` + LogLevel string `short:"l" description:"Log level"` + EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'"` + ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL"` + DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"` + ProvidersThrottleDuration time.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time."` + MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"` + Retry *Retry `description:"Enable retry sending request if network error"` Docker *provider.Docker `description:"Enable Docker backend"` File *provider.File `description:"Enable File backend"` Web *WebProvider `description:"Enable Web backend"` @@ -49,7 +49,7 @@ type DefaultEntryPoints []string // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. func (dep *DefaultEntryPoints) String() string { - //TODO : + //TODO : The string returned should be formatted in such way that the func Set below could parse it. return fmt.Sprintf("%#v", dep) } @@ -86,8 +86,10 @@ type EntryPoints map[string]*EntryPoint // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. func (ep *EntryPoints) String() string { - //TODO : - return "" + //TODO : The string returned should be formatted in such way that the func Set below could parse it. + //Like this --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' + //But the Set func parses entrypoint one by one only + return fmt.Sprintf("%+v", *ep) } // Set is the method to set the flag value, part of the flag.Value interface. @@ -208,12 +210,12 @@ type Certificate struct { // Retry contains request retry config type Retry struct { - Attempts int - MaxMem int64 + Attempts int `description:"Number of attempts"` + MaxMem int64 `description:"Maximum request body to be stored in memory in Mo"` } -// NewTraefikPointersConfiguration creates a TraefikConfiguration with pointers default values -func NewTraefikPointersConfiguration() *TraefikConfiguration { +// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values +func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { //default Docker var defaultDocker provider.Docker defaultDocker.Watch = true @@ -281,6 +283,7 @@ func NewTraefikPointersConfiguration() *TraefikConfiguration { Zookeeper: &defaultZookeeper, Boltdb: &defaultBoltDb, Kubernetes: &defaultKubernetes, + Retry: &Retry{MaxMem: 2}, } return &TraefikConfiguration{ GlobalConfiguration: defaultConfiguration, diff --git a/provider/zk.go b/provider/zk.go index e8164a75f..77b28100f 100644 --- a/provider/zk.go +++ b/provider/zk.go @@ -9,7 +9,7 @@ import ( // Zookepper holds configurations of the Zookepper provider. type Zookepper struct { - Kv `description:"go through"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/traefik.go b/traefik.go index fbc6a4f7f..898cac622 100644 --- a/traefik.go +++ b/traefik.go @@ -5,6 +5,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/containous/flaeg" "github.com/containous/staert" + "github.com/containous/traefik/acme" "github.com/containous/traefik/middlewares" "github.com/containous/traefik/provider" fmtlog "log" @@ -20,7 +21,7 @@ func main() { //traefik config inits traefikConfiguration := NewTraefikConfiguration() - traefikPointersConfiguration := NewTraefikPointersConfiguration() + traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration() //traefik Command init traefikCmd := &flaeg.Command{ Name: "traefik", @@ -52,6 +53,8 @@ Complete documentation is available at https://traefik.io`, f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{}) f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{}) f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{}) + f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{}) + //add version command f.AddCommand(versionCmd) if _, err := f.Parse(traefikCmd); err != nil { From 7f6b2b80f8850cbb4cefec9b8b2a004873ab1d31 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 26 May 2016 11:09:20 +0200 Subject: [PATCH 16/19] rm useless TestNoOrInexistentConfigShouldNotFail Signed-off-by: Martin --- integration/basic_test.go | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/integration/basic_test.go b/integration/basic_test.go index 0e7220279..7e5549174 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -5,7 +5,6 @@ import ( "os/exec" "time" - "fmt" "github.com/go-check/check" "bytes" @@ -15,34 +14,6 @@ import ( // SimpleSuite type SimpleSuite struct{ BaseSuite } -func (s *SimpleSuite) TestNoOrInexistentConfigShouldNotFail(c *check.C) { - cmd := exec.Command(traefikBinary) - - var b bytes.Buffer - cmd.Stdout = &b - cmd.Stderr = &b - - cmd.Start() - time.Sleep(500 * time.Millisecond) - output := b.Bytes() - - c.Assert(string(output), checker.Not(checker.Contains), "No configuration file found") - cmd.Process.Kill() - - nonExistentFile := "non/existent/file.toml" - cmd = exec.Command(traefikBinary, "--configFile="+nonExistentFile) - - cmd.Stdout = &b - cmd.Stderr = &b - - cmd.Start() - time.Sleep(500 * time.Millisecond) - output = b.Bytes() - - c.Assert(string(output), checker.Not(checker.Contains), fmt.Sprintf("Error reading configuration file: open %s: no such file or directory", nonExistentFile)) - cmd.Process.Kill() -} - func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { cmd := exec.Command(traefikBinary, "--configFile=fixtures/invalid_configuration.toml") From cc2735f7332eaf627e978eedd4267f91722608e9 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 26 May 2016 15:55:50 +0200 Subject: [PATCH 17/19] add Debug StructTag Signed-off-by: Martin --- configuration.go | 2 +- provider/kubernetes_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration.go b/configuration.go index 201a61fe8..35a7234de 100644 --- a/configuration.go +++ b/configuration.go @@ -21,7 +21,7 @@ type TraefikConfiguration struct { // It's populated from the traefik configuration file passed as an argument to the binary. type GlobalConfiguration struct { GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."` - Debug bool + Debug bool `short:"d" description:"Enable debug mode"` AccessLogsFile string `description:"Access logs file"` TraefikLogsFile string `description:"Traefik logs file"` LogLevel string `short:"l" description:"Log level"` diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index 8426b73ad..25e48b8d7 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -1205,7 +1205,7 @@ func TestHostlessIngress(t *testing.T) { services: services, watchChan: watchChan, } - provider := Kubernetes{disablePassHostHeaders: true} + provider := Kubernetes{DisablePassHostHeaders: true} actual, err := provider.loadIngresses(client) if err != nil { t.Fatalf("error %+v", err) From c5084fd02523d7025417dfacecda5360e0c66eb6 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 27 May 2016 10:04:56 +0200 Subject: [PATCH 18/19] update staert + glide pin version --- glide.lock | 8 ++++---- glide.yaml | 2 ++ traefik.go | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/glide.lock b/glide.lock index a88e1030c..cd8969fca 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 50e873651126f0af9204fd8cc11f10917ce56b76479310ada578f5487162e0c4 -updated: 2016-05-24T15:42:45.810673781+02:00 +hash: 660249b4d5cbcfd0cb0a2d9d39ce798ec8df7d3277c53ed4043fe2f61b29eeb9 +updated: 2016-05-27T09:59:17.855890752+02:00 imports: - name: github.com/boltdb/bolt version: dfb21201d9270c1082d5fb0f07f500311ff72f18 @@ -18,7 +18,7 @@ imports: - name: github.com/codegangsta/negroni version: ffbc66b612ee3eac2eba29aedce4c3a65e4dd0a1 - name: github.com/containous/flaeg - version: e390ea90e4ee21ff73fb1630b8c50a843a6d7674 + version: c425b9d758df1864ca838dbd433f1cf8f5097d51 - name: github.com/containous/oxy version: 183212964e13e7b8afe01a08b193d04300554a68 subpackages: @@ -29,7 +29,7 @@ imports: - stream - utils - name: github.com/containous/staert - version: f0da5ea8404f0d3999d72ba8f8736d3aa4b8c8f1 + version: ff272631ecfc9c22490b651fea0f08d364d46518 - name: github.com/coreos/etcd version: c400d05d0aa73e21e431c16145e558d624098018 subpackages: diff --git a/glide.yaml b/glide.yaml index df8f8dcf5..d64ed3c4c 100644 --- a/glide.yaml +++ b/glide.yaml @@ -8,6 +8,7 @@ import: - package: github.com/cenkalti/backoff - package: github.com/codegangsta/negroni - package: github.com/containous/flaeg + version: c425b9d758df1864ca838dbd433f1cf8f5097d51 - package: github.com/containous/oxy subpackages: - cbreaker @@ -17,6 +18,7 @@ import: - stream - utils - package: github.com/containous/staert + version: ff272631ecfc9c22490b651fea0f08d364d46518 - package: github.com/docker/engine-api version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: diff --git a/traefik.go b/traefik.go index 898cac622..a313c6466 100644 --- a/traefik.go +++ b/traefik.go @@ -70,7 +70,7 @@ Complete documentation is available at https://traefik.io`, //add sources to staert s.AddSource(toml) s.AddSource(f) - if _, err := s.GetConfig(); err != nil { + if _, err := s.LoadConfig(); err != nil { fmtlog.Println(err) } if traefikConfiguration.File != nil && len(traefikConfiguration.File.Filename) == 0 { From 4776fa136112e714747391b70a296dff2e913831 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 27 May 2016 11:13:34 +0200 Subject: [PATCH 19/19] add parsers tests Signed-off-by: Martin --- acme/acme.go | 9 ++++--- acme/acme_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++ configuration.go | 7 +----- 3 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 acme/acme_test.go diff --git a/acme/acme.go b/acme/acme.go index 081e4d8e4..99e340ae4 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -163,7 +163,7 @@ func (dc *DomainsCertificate) needRenew() bool { // ACME allows to connect to lets encrypt and retrieve certs type ACME struct { Email string `description:"Email address used for registration"` - Domains []Domain `description:"SANs (alternative domains) to each main domain"` + 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'"` StorageFile string `description:"File used for certificates storage."` 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."` CAServer string `description:"CA server to use."` @@ -181,12 +181,15 @@ func (ds *Domains) Set(str string) error { } // get function slice := strings.FieldsFunc(str, fargs) - if len(slice) >= 2 { + if len(slice) < 1 { return fmt.Errorf("Parse error ACME.Domain. Imposible to parse %s", str) } d := Domain{ Main: slice[0], - SANs: slice[1:], + SANs: []string{}, + } + if len(slice) > 1 { + d.SANs = slice[1:] } *ds = append(*ds, d) return nil diff --git a/acme/acme_test.go b/acme/acme_test.go new file mode 100644 index 000000000..302c5866e --- /dev/null +++ b/acme/acme_test.go @@ -0,0 +1,61 @@ +package acme + +import ( + "reflect" + "testing" +) + +func TestDomainsSet(t *testing.T) { + checkMap := map[string]Domains{ + "": {}, + "foo.com": {Domain{Main: "foo.com", SANs: []string{}}}, + "foo.com,bar.net": {Domain{Main: "foo.com", SANs: []string{"bar.net"}}}, + "foo.com,bar1.net,bar2.net,bar3.net": {Domain{Main: "foo.com", SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}}, + } + for in, check := range checkMap { + ds := Domains{} + ds.Set(in) + if !reflect.DeepEqual(check, ds) { + t.Errorf("Expected %+v\nGo %+v", check, ds) + } + } +} + +func TestDomainsSetAppend(t *testing.T) { + inSlice := []string{ + "", + "foo1.com", + "foo2.com,bar.net", + "foo3.com,bar1.net,bar2.net,bar3.net", + } + checkSlice := []Domains{ + {}, + { + Domain{ + Main: "foo1.com", + SANs: []string{}}}, + { + Domain{ + Main: "foo1.com", + SANs: []string{}}, + Domain{ + Main: "foo2.com", + SANs: []string{"bar.net"}}}, + { + Domain{ + Main: "foo1.com", + SANs: []string{}}, + Domain{ + Main: "foo2.com", + SANs: []string{"bar.net"}}, + Domain{Main: "foo3.com", + SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}}, + } + ds := Domains{} + for i, in := range inSlice { + ds.Set(in) + if !reflect.DeepEqual(checkSlice[i], ds) { + t.Errorf("Expected %s %+v\nGo %+v", in, checkSlice[i], ds) + } + } +} diff --git a/configuration.go b/configuration.go index 35a7234de..72703e177 100644 --- a/configuration.go +++ b/configuration.go @@ -49,8 +49,7 @@ type DefaultEntryPoints []string // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. func (dep *DefaultEntryPoints) String() string { - //TODO : The string returned should be formatted in such way that the func Set below could parse it. - return fmt.Sprintf("%#v", dep) + return strings.Join(*dep, ",") } // Set is the method to set the flag value, part of the flag.Value interface. @@ -86,9 +85,6 @@ type EntryPoints map[string]*EntryPoint // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. func (ep *EntryPoints) String() string { - //TODO : The string returned should be formatted in such way that the func Set below could parse it. - //Like this --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' - //But the Set func parses entrypoint one by one only return fmt.Sprintf("%+v", *ep) } @@ -176,7 +172,6 @@ type Certificates []Certificate // The String method's output will be used in diagnostics. func (certs *Certificates) String() string { if len(*certs) == 0 { - //TODO : return "" } return (*certs)[0].CertFile + "," + (*certs)[0].KeyFile