func (provider *Docker) containerFilter(container dockerData) bool { _, err := strconv.Atoi(container.Labels["traefik.port"]) if len(container.NetworkSettings.Ports) == 0 && err != nil { log.Debugf("Filtering container without port and no traefik.port label %s", container.Name) return false } if !isContainerEnabled(container, provider.ExposedByDefault) { log.Debugf("Filtering disabled container %s", container.Name) return false } constraintTags := strings.Split(container.Labels["traefik.tags"], ",") if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok { if failingConstraint != nil { log.Debugf("Container %v pruned by '%v' constraint", container.Name, failingConstraint.String()) } return false } if container.Health != "" && container.Health != "healthy" { log.Debugf("Filtering unhealthy or starting container %s", container.Name) return false } return true }
// Provide allows the provider to provide configurations to traefik // using the given configuration channel. func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { k8sClient, err := provider.newK8sClient() if err != nil { return err } provider.Constraints = append(provider.Constraints, constraints...) pool.Go(func(stop chan bool) { operation := func() error { for { stopWatch := make(chan struct{}, 1) defer close(stopWatch) log.Debugf("Using label selector: '%s'", provider.LabelSelector) eventsChan, err := k8sClient.WatchAll(provider.LabelSelector, stopWatch) if err != nil { log.Errorf("Error watching kubernetes events: %v", err) timer := time.NewTimer(1 * time.Second) select { case <-timer.C: return err case <-stop: return nil } } for { select { case <-stop: return nil case event := <-eventsChan: log.Debugf("Received event from kubernetes %+v", event) templateObjects, err := provider.loadIngresses(k8sClient) if err != nil { return err } if reflect.DeepEqual(provider.lastConfiguration.Get(), templateObjects) { log.Debugf("Skipping event from kubernetes %+v", event) } else { provider.lastConfiguration.Set(templateObjects) configurationChan <- types.ConfigMessage{ ProviderName: "kubernetes", Configuration: provider.loadConfig(*templateObjects), } } } } } } notify := func(err error, time time.Duration) { log.Errorf("Kubernetes connection error %+v, retrying in %s", err, time) } err := backoff.RetryNotify(operation, job.NewBackOff(backoff.NewExponentialBackOff()), notify) if err != nil { log.Errorf("Cannot connect to Kubernetes server %+v", err) } }) return nil }
func (provider *Kv) get(defaultValue string, keys ...string) string { joinedKeys := strings.Join(keys, "") keyPair, err := provider.kvclient.Get(strings.TrimPrefix(joinedKeys, "/")) if err != nil { log.Debugf("Cannot get key %s %s, setting default %s", joinedKeys, err, defaultValue) return defaultValue } else if keyPair == nil { log.Debugf("Cannot get key %s, setting default %s", joinedKeys, defaultValue) return defaultValue } return string(keyPair.Value) }
func (provider *Kv) splitGet(keys ...string) []string { joinedKeys := strings.Join(keys, "") keyPair, err := provider.kvclient.Get(joinedKeys) if err != nil { log.Debugf("Cannot get key %s %s, setting default empty", joinedKeys, err) return []string{} } else if keyPair == nil { log.Debugf("Cannot get key %s, setting default %empty", joinedKeys) return []string{} } return strings.Split(string(keyPair.Value), ",") }
func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { domain := types.CanonicalDomain(clientHello.ServerName) account := a.store.Get().(*Account) if certificateResource, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok { return certificateResource.tlsCert, nil } certificate, err := a.getDomainsCertificates([]string{domain}) if err != nil { return nil, err } log.Debugf("Got certificate on demand for domain %s", domain) transaction, object, err := a.store.Begin() if err != nil { return nil, err } account = object.(*Account) cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, Domain{Main: domain}) if err != nil { return nil, err } if err = transaction.Commit(account); err != nil { return nil, err } return cert.tlsCert, nil }
// Commit allows to set an object in the KV store func (s *datastoreTransaction) Commit(object Object) error { s.localLock.Lock() defer s.localLock.Unlock() if s.dirty { return fmt.Errorf("transaction already used, please begin a new one") } s.Datastore.meta.object = object err := s.Datastore.meta.Marshall() if err != nil { return err } err = s.kv.StoreConfig(s.Datastore.meta) if err != nil { return err } err = s.remoteLock.Unlock() if err != nil { return err } s.dirty = true log.Debugf("Transaction committed %s", s.id) return nil }
func (a *Authenticator) secretDigest(user, realm string) string { if secret, ok := a.users[user+":"+realm]; ok { return secret } log.Debugf("User not found: %s:%s", user, realm) return "" }
func (provider *ConsulCatalog) healthyNodes(service string) (catalogUpdate, error) { health := provider.client.Health() opts := &api.QueryOptions{} data, _, err := health.Service(service, "", true, opts) if err != nil { log.WithError(err).Errorf("Failed to fetch details of " + service) return catalogUpdate{}, err } nodes := fun.Filter(func(node *api.ServiceEntry) bool { constraintTags := provider.getContraintTags(node.Service.Tags) ok, failingConstraint := provider.MatchConstraints(constraintTags) if ok == false && failingConstraint != nil { log.Debugf("Service %v pruned by '%v' constraint", service, failingConstraint.String()) } return ok }, data).([]*api.ServiceEntry) //Merge tags of nodes matching constraints, in a single slice. tags := fun.Foldl(func(node *api.ServiceEntry, set []string) []string { return fun.Keys(fun.Union( fun.Set(set), fun.Set(node.Service.Tags), ).(map[string]bool)).([]string) }, []string{}, nodes).([]string) return catalogUpdate{ Service: &serviceUpdate{ ServiceName: service, Attributes: tags, }, Nodes: nodes, }, nil }
func (a *Authenticator) secretBasic(user, realm string) string { if secret, ok := a.users[user]; ok { return secret } log.Debugf("User not found: %s", user) return "" }
func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (http.Handler, error) { regex := entryPoint.Redirect.Regex replacement := entryPoint.Redirect.Replacement if len(entryPoint.Redirect.EntryPoint) > 0 { regex = "^(?:https?:\\/\\/)?([\\da-z\\.-]+)(?::\\d+)?(.*)$" if server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint] == nil { return nil, errors.New("Unknown entrypoint " + entryPoint.Redirect.EntryPoint) } protocol := "http" if server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].TLS != nil { protocol = "https" } r, _ := regexp.Compile("(:\\d+)") match := r.FindStringSubmatch(server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].Address) if len(match) == 0 { return nil, errors.New("Bad Address format: " + server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].Address) } replacement = protocol + "://$1" + match[0] + "$2" } rewrite, err := middlewares.NewRewrite(regex, replacement, true) if err != nil { return nil, err } log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", entryPointName, entryPoint.Redirect.EntryPoint, regex, replacement) negroni := negroni.New() negroni.Use(rewrite) return negroni, nil }
// LoadCertificateForDomains loads certificates from ACME for given domains func (a *ACME) LoadCertificateForDomains(domains []string) { domains = fun.Map(types.CanonicalDomain, domains).([]string) safe.Go(func() { operation := func() error { if a.client == nil { return fmt.Errorf("ACME client still not built") } return nil } notify := func(err error, time time.Duration) { log.Errorf("Error getting ACME client: %v, retrying in %s", err, time) } ebo := backoff.NewExponentialBackOff() ebo.MaxElapsedTime = 30 * time.Second err := backoff.RetryNotify(operation, ebo, notify) if err != nil { log.Errorf("Error getting ACME client: %v", err) return } account := a.store.Get().(*Account) var domain Domain if len(domains) == 0 { // no domain return } else if len(domains) > 1 { domain = Domain{Main: domains[0], SANs: domains[1:]} } else { domain = Domain{Main: domains[0]} } if _, exists := account.DomainsCertificate.exists(domain); exists { // domain already exists return } certificate, err := a.getDomainsCertificates(domains) if err != nil { log.Errorf("Error getting ACME certificates %+v : %v", domains, err) return } log.Debugf("Got certificate for domains %+v", domains) transaction, object, err := a.store.Begin() if err != nil { log.Errorf("Error creating transaction %+v : %v", domains, err) return } account = object.(*Account) _, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain) if err != nil { log.Errorf("Error adding ACME certificates %+v : %v", domains, err) return } if err = transaction.Commit(account); err != nil { log.Errorf("Error Saving ACME account %+v: %v", account, err) return } }) }
// NewAuthenticator builds a new Autenticator given a config func NewAuthenticator(authConfig *types.Auth) (*Authenticator, error) { if authConfig == nil { return nil, fmt.Errorf("Error creating Authenticator: auth is nil") } var err error authenticator := Authenticator{} if authConfig.Basic != nil { authenticator.users, err = parserBasicUsers(authConfig.Basic.Users) if err != nil { return nil, err } basicAuth := auth.NewBasicAuthenticator("traefik", authenticator.secretBasic) authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if username := basicAuth.CheckAuth(r); username == "" { log.Debugf("Basic auth failed...") basicAuth.RequireAuth(w, r) } else { log.Debugf("Basic auth success...") if authConfig.HeaderField != "" { r.Header[authConfig.HeaderField] = []string{username} } next.ServeHTTP(w, r) } }) } else if authConfig.Digest != nil { authenticator.users, err = parserDigestUsers(authConfig.Digest.Users) if err != nil { return nil, err } digestAuth := auth.NewDigestAuthenticator("traefik", authenticator.secretDigest) authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if username, _ := digestAuth.CheckAuth(r); username == "" { log.Debugf("Digest auth failed...") digestAuth.RequireAuth(w, r) } else { log.Debugf("Digest auth success...") if authConfig.HeaderField != "" { r.Header[authConfig.HeaderField] = []string{username} } next.ServeHTTP(w, r) } }) } return &authenticator, nil }
// Participate tries to be a leader func (l *Leadership) Participate(pool *safe.Pool) { pool.GoCtx(func(ctx context.Context) { log.Debugf("Node %s running for election", l.Cluster.Node) defer log.Debugf("Node %s no more running for election", l.Cluster.Node) backOff := backoff.NewExponentialBackOff() operation := func() error { return l.run(ctx, l.candidate) } notify := func(err error, time time.Duration) { log.Errorf("Leadership election error %+v, retrying in %s", err, time) } err := backoff.RetryNotify(operation, backOff, notify) if err != nil { log.Errorf("Cannot elect leadership %+v", err) } }) }
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) { domains = fun.Map(types.CanonicalDomain, domains).([]string) log.Debugf("Loading ACME certificates %s...", domains) bundle := true certificate, failures := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple) if len(failures) > 0 { log.Error(failures) return nil, fmt.Errorf("Cannot obtain certificates %s+v", failures) } log.Debugf("Loaded ACME certificates %s", domains) return &Certificate{ Domain: certificate.Domain, CertURL: certificate.CertURL, CertStableURL: certificate.CertStableURL, PrivateKey: certificate.PrivateKey, Certificate: certificate.Certificate, }, nil }
func (server *Server) listenProviders(stop chan bool) { lastReceivedConfiguration := safe.New(time.Unix(0, 0)) lastConfigs := cmap.New() for { select { case <-stop: return case configMsg, ok := <-server.configurationChan: if !ok { return } server.defaultConfigurationValues(configMsg.Configuration) currentConfigurations := server.currentConfigurations.Get().(configs) jsonConf, _ := json.Marshal(configMsg.Configuration) log.Debugf("Configuration received from provider %s: %s", configMsg.ProviderName, string(jsonConf)) if configMsg.Configuration == nil || configMsg.Configuration.Backends == nil && configMsg.Configuration.Frontends == nil { log.Infof("Skipping empty Configuration for provider %s", configMsg.ProviderName) } else if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) { log.Infof("Skipping same configuration for provider %s", configMsg.ProviderName) } else { lastConfigs.Set(configMsg.ProviderName, &configMsg) lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time) if time.Now().After(lastReceivedConfigurationValue.Add(time.Duration(server.globalConfiguration.ProvidersThrottleDuration))) { log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String()) // last config received more than n s ago server.configurationValidatedChan <- configMsg } else { log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String()) safe.Go(func() { <-time.After(server.globalConfiguration.ProvidersThrottleDuration) lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time) if time.Now().After(lastReceivedConfigurationValue.Add(time.Duration(server.globalConfiguration.ProvidersThrottleDuration))) { log.Debugf("Waited for %s config, OK", configMsg.ProviderName) if lastConfig, ok := lastConfigs.Get(configMsg.ProviderName); ok { server.configurationValidatedChan <- *lastConfig.(*types.ConfigMessage) } } }) } lastReceivedConfiguration.Set(time.Now()) } } } }
func (a *ACME) renewCertificates() error { log.Debugf("Testing certificate renew...") account := a.store.Get().(*Account) for _, certificateResource := range account.DomainsCertificate.Certs { if certificateResource.needRenew() { log.Debugf("Renewing certificate %+v", certificateResource.Domains) renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{ Domain: certificateResource.Certificate.Domain, CertURL: certificateResource.Certificate.CertURL, CertStableURL: certificateResource.Certificate.CertStableURL, PrivateKey: certificateResource.Certificate.PrivateKey, Certificate: certificateResource.Certificate.Certificate, }, true, OSCPMustStaple) if err != nil { log.Errorf("Error renewing certificate: %v", err) continue } log.Debugf("Renewed certificate %+v", certificateResource.Domains) renewedACMECert := &Certificate{ Domain: renewedCert.Domain, CertURL: renewedCert.CertURL, CertStableURL: renewedCert.CertStableURL, PrivateKey: renewedCert.PrivateKey, Certificate: renewedCert.Certificate, } transaction, object, err := a.store.Begin() if err != nil { return err } account = object.(*Account) err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains) if err != nil { log.Errorf("Error renewing certificate: %v", err) continue } if err = transaction.Commit(account); err != nil { log.Errorf("Error Saving ACME account %+v: %s", account, err.Error()) continue } } } return nil }
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { domain := types.CanonicalDomain(clientHello.ServerName) account := a.store.Get().(*Account) if challengeCert, ok := a.challengeProvider.getCertificate(domain); ok { log.Debugf("ACME got challenge %s", domain) return challengeCert, nil } if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok { log.Debugf("ACME got domain cert %s", domain) return domainCert.tlsCert, nil } if a.OnDemand { if a.checkOnDemandDomain != nil && !a.checkOnDemandDomain(domain) { return nil, nil } return a.loadCertificateOnDemand(clientHello) } log.Debugf("ACME got nothing %s", domain) return nil, nil }
func (c *challengeProvider) CleanUp(domain, token, keyAuth string) error { log.Debugf("Challenge CleanUp %s", domain) c.lock.Lock() defer c.lock.Unlock() transaction, object, err := c.store.Begin() if err != nil { return err } account := object.(*Account) delete(account.ChallengeCerts, domain) return transaction.Commit(account) }
// Stop stops the server func (server *Server) Stop() { for serverEntryPointName, serverEntryPoint := range server.serverEntryPoints { ctx, cancel := context.WithTimeout(context.Background(), time.Duration(server.globalConfiguration.GraceTimeOut)*time.Second) go func() { log.Debugf("Waiting %d seconds before killing connections on entrypoint %s...", 30, serverEntryPointName) serverEntryPoint.httpServer.BlockingClose() cancel() }() <-ctx.Done() } server.stopChan <- true }
// Begin creates a transaction with the KV store. func (d *Datastore) Begin() (Transaction, Object, error) { id := uuid.NewV4().String() log.Debugf("Transaction %s begins", id) remoteLock, err := d.kv.NewLock(d.lockKey, &store.LockOptions{TTL: 20 * time.Second, Value: []byte(id)}) if err != nil { return nil, nil, err } stopCh := make(chan struct{}) ctx, cancel := context.WithCancel(d.ctx) var errLock error go func() { _, errLock = remoteLock.Lock(stopCh) cancel() }() select { case <-ctx.Done(): if errLock != nil { return nil, nil, errLock } case <-d.ctx.Done(): stopCh <- struct{}{} return nil, nil, d.ctx.Err() } // we got the lock! Now make sure we are synced with KV store operation := func() error { meta := d.get() if meta.Lock != id { return fmt.Errorf("Object lock value: expected %s, got %s", id, meta.Lock) } return nil } notify := func(err error, time time.Duration) { log.Errorf("Datastore sync error: %v, retrying in %s", err, time) err = d.reload() if err != nil { log.Errorf("Error reloading: %+v", err) } } ebo := backoff.NewExponentialBackOff() ebo.MaxElapsedTime = 60 * time.Second err = backoff.RetryNotify(operation, ebo, notify) if err != nil { return nil, nil, fmt.Errorf("Datastore cannot sync: %v", err) } // we synced with KV store, we can now return Setter return &datastoreTransaction{ Datastore: d, remoteLock: remoteLock, id: id, }, d.meta.object, nil }
func dnsOverrideDelay(delay int) error { var err error if delay > 0 { log.Debugf("Delaying %d seconds rather than validating DNS propagation", delay) acme.PreCheckDNS = func(_, _ string) (bool, error) { time.Sleep(time.Duration(delay) * time.Second) return true, nil } } else if delay < 0 { err = fmt.Errorf("Invalid negative DelayDontCheckDNS: %d", delay) } return err }
func mesosTaskFilter(task state.Task, exposedByDefaultFlag bool) bool { if len(task.DiscoveryInfo.Ports.DiscoveryPorts) == 0 { log.Debugf("Filtering mesos task without port %s", task.Name) return false } if !isMesosApplicationEnabled(task, exposedByDefaultFlag) { log.Debugf("Filtering disabled mesos task %s", task.DiscoveryInfo.Name) return false } //filter indeterminable task port portIndexLabel := labels(task, "traefik.portIndex") portValueLabel := labels(task, "traefik.port") if portIndexLabel != "" && portValueLabel != "" { log.Debugf("Filtering mesos task %s specifying both traefik.portIndex and traefik.port labels", task.Name) return false } if portIndexLabel != "" { index, err := strconv.Atoi(labels(task, "traefik.portIndex")) if err != nil || index < 0 || index > len(task.DiscoveryInfo.Ports.DiscoveryPorts)-1 { log.Debugf("Filtering mesos task %s with unexpected value for traefik.portIndex label", task.Name) return false } } if portValueLabel != "" { port, err := strconv.Atoi(labels(task, "traefik.port")) if err != nil { log.Debugf("Filtering mesos task %s with unexpected value for traefik.port label", task.Name) return false } var foundPort bool for _, exposedPort := range task.DiscoveryInfo.Ports.DiscoveryPorts { if port == exposedPort.Number { foundPort = true break } } if !foundPort { log.Debugf("Filtering mesos task %s without a matching port for traefik.port label", task.Name) return false } } //filter healthchecks if task.Statuses != nil && len(task.Statuses) > 0 && task.Statuses[0].Healthy != nil && !*task.Statuses[0].Healthy { log.Debugf("Filtering mesos task %s with bad healthcheck", task.DiscoveryInfo.Name) return false } return true }
func (provider *Kv) list(keys ...string) []string { joinedKeys := strings.Join(keys, "") keysPairs, err := provider.kvclient.List(joinedKeys) if err != nil { log.Debugf("Cannot get keys %s %s ", joinedKeys, err) return nil } directoryKeys := make(map[string]string) for _, key := range keysPairs { directory := strings.Split(strings.TrimPrefix(key.Key, joinedKeys), "/")[0] directoryKeys[directory] = joinedKeys + directory } return fun.Values(directoryKeys).([]string) }
func detectMasters(zk string, masters []string) <-chan []string { changed := make(chan []string, 1) if zk != "" { log.Debugf("Starting master detector for ZK ", zk) if md, err := detector.New(zk); err != nil { log.Errorf("failed to create master detector: %v", err) } else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil { log.Errorf("failed to initialize master detector: %v", err) } } else { changed <- masters } return changed }
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { log.Debugf("Building ACME client...") caServer := "https://acme-v01.api.letsencrypt.org/directory" if len(a.CAServer) > 0 { caServer = a.CAServer } client, err := acme.NewClient(caServer, account, acme.RSA4096) if err != nil { return nil, err } if len(a.DNSProvider) > 0 { log.Debugf("Using DNS Challenge provider: %s", a.DNSProvider) err = dnsOverrideDelay(a.DelayDontCheckDNS) if err != nil { return nil, err } var provider acme.ChallengeProvider provider, err = dns.NewDNSChallengeProviderByName(a.DNSProvider) if err != nil { return nil, err } client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) err = client.SetChallengeProvider(acme.DNS01, provider) } else { client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) err = client.SetChallengeProvider(acme.TLSSNI01, a.challengeProvider) } if err != nil { return nil, err } return client, nil }
func (retry *Retry) ServeHTTP(rw http.ResponseWriter, r *http.Request) { attempts := 1 for { recorder := NewRecorder() recorder.responseWriter = rw retry.next.ServeHTTP(recorder, r) if !isNetworkError(recorder.Code) || attempts >= retry.attempts { utils.CopyHeaders(rw.Header(), recorder.Header()) rw.WriteHeader(recorder.Code) rw.Write(recorder.Body.Bytes()) break } attempts++ log.Debugf("New attempt %d for request: %v", attempts, r.URL) } }
func (d *Datastore) reload() error { log.Debugf("Datastore reload") d.localLock.Lock() err := d.kv.LoadConfig(d.meta) if err != nil { d.localLock.Unlock() return err } err = d.meta.unmarshall() if err != nil { d.localLock.Unlock() return err } d.localLock.Unlock() return nil }
func (server *Server) defaultConfigurationValues(configuration *types.Configuration) { if configuration == nil || configuration.Frontends == nil { return } for _, frontend := range configuration.Frontends { // default endpoints if not defined in frontends if len(frontend.EntryPoints) == 0 { frontend.EntryPoints = server.globalConfiguration.DefaultEntryPoints } } for backendName, backend := range configuration.Backends { _, err := types.NewLoadBalancerMethod(backend.LoadBalancer) if err != nil { log.Debugf("Load balancer method '%+v' for backend %s: %v. Using default wrr.", backend.LoadBalancer, backendName, err) backend.LoadBalancer = &types.LoadBalancer{Method: "wrr"} } } }
func (provider *Kv) checkConstraints(keys ...string) bool { joinedKeys := strings.Join(keys, "") keyPair, err := provider.kvclient.Get(joinedKeys) value := "" if err == nil && keyPair != nil && keyPair.Value != nil { value = string(keyPair.Value) } constraintTags := strings.Split(value, ",") ok, failingConstraint := provider.MatchConstraints(constraintTags) if ok == false { if failingConstraint != nil { log.Debugf("Constraint %v not matching with following tags: %v", failingConstraint.String(), value) } return false } return true }
func (provider *Marathon) applicationFilter(app marathon.Application, filteredTasks []marathon.Task) bool { label, _ := provider.getLabel(app, "traefik.tags") constraintTags := strings.Split(label, ",") if provider.MarathonLBCompatibility { if label, err := provider.getLabel(app, "HAPROXY_GROUP"); err == nil { constraintTags = append(constraintTags, label) } } if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok { if failingConstraint != nil { log.Debugf("Application %v pruned by '%v' constraint", app.ID, failingConstraint.String()) } return false } return fun.Exists(func(task marathon.Task) bool { return task.AppID == app.ID }, filteredTasks) }