Merge v1.1.1 into master

Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
Emile Vauge 2016-12-05 21:25:46 +01:00
commit dca08af003
No known key found for this signature in database
GPG key ID: D808B4C167352E59
41 changed files with 760 additions and 170 deletions

View file

@ -17,7 +17,7 @@ type BoltDb struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *BoltDb) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *BoltDb) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := provider.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)

View file

@ -17,7 +17,7 @@ type Consul struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Consul) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Consul) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := provider.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)

View file

@ -317,7 +317,7 @@ func (provider *ConsulCatalog) watch(configurationChan chan<- types.ConfigMessag
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *ConsulCatalog) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *ConsulCatalog) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
config := api.DefaultConfig()
config.Address = provider.Endpoint
client, err := api.NewClient(config)

View file

@ -61,7 +61,7 @@ func TestConsulCatalogGetAttribute(t *testing.T) {
"traefik.backend.weight=42",
},
key: "backend.weight",
defaultValue: "",
defaultValue: "0",
expected: "42",
},
{
@ -70,8 +70,8 @@ func TestConsulCatalogGetAttribute(t *testing.T) {
"traefik.backend.wei=42",
},
key: "backend.weight",
defaultValue: "",
expected: "",
defaultValue: "0",
expected: "0",
},
}

View file

@ -113,7 +113,7 @@ func (provider *Docker) createClient() (client.APIClient, error) {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
provider.Constraints = append(provider.Constraints, constraints...)
// TODO register this routine in pool, and watch for stop channel
safe.Go(func() {
@ -363,10 +363,6 @@ func (provider *Docker) containerFilter(container dockerData) bool {
log.Debugf("Filtering container without port and no traefik.port label %s", container.Name)
return false
}
if len(container.NetworkSettings.Ports) > 1 && err != nil {
log.Debugf("Filtering container with more than 1 port and no traefik.port label %s", container.Name)
return false
}
if !isContainerEnabled(container, provider.ExposedByDefault) {
log.Debugf("Filtering disabled container %s", container.Name)
@ -405,7 +401,7 @@ func (provider *Docker) getFrontendRule(container dockerData) string {
func (provider *Docker) getBackend(container dockerData) string {
if label, err := getLabel(container, "traefik.backend"); err == nil {
return label
return normalize(label)
}
return normalize(container.Name)
}
@ -458,7 +454,7 @@ func (provider *Docker) getWeight(container dockerData) string {
if label, err := getLabel(container, "traefik.weight"); err == nil {
return label
}
return "1"
return "0"
}
func (provider *Docker) getSticky(container dockerData) string {
@ -563,6 +559,10 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
if container.ContainerJSONBase.HostConfig != nil {
dockerData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode
}
if container.State != nil && container.State.Health != nil {
dockerData.Health = container.State.Health.Status
}
}
if container.Config != nil && container.Config.Labels != nil {
@ -586,10 +586,6 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
}
if container.State != nil && container.State.Health != nil {
dockerData.Health = container.State.Health.Status
}
return dockerData
}

View file

@ -390,6 +390,27 @@ func TestDockerGetPort(t *testing.T) {
},
expected: "8080",
},
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "test-multi-ports",
},
Config: &container.Config{
Labels: map[string]string{
"traefik.port": "8080",
},
},
NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"8080/tcp": {},
"80/tcp": {},
},
},
},
},
expected: "8080",
},
}
for _, e := range containers {
@ -415,7 +436,7 @@ func TestDockerGetWeight(t *testing.T) {
},
Config: &container.Config{},
},
expected: "1",
expected: "0",
},
{
container: docker.ContainerJSON{
@ -735,7 +756,7 @@ func TestDockerTraefikFilter(t *testing.T) {
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "container",
Name: "container-multi-ports",
},
Config: &container.Config{},
NetworkSettings: &docker.NetworkSettings{
@ -748,7 +769,7 @@ func TestDockerTraefikFilter(t *testing.T) {
},
},
exposedByDefault: true,
expected: false,
expected: true,
},
{
container: docker.ContainerJSON{
@ -951,7 +972,7 @@ func TestDockerLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@ -1033,11 +1054,11 @@ func TestDockerLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
"server-test2": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@ -1091,7 +1112,7 @@ func TestDockerLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: &types.CircuitBreaker{
@ -1506,7 +1527,7 @@ func TestSwarmGetWeight(t *testing.T) {
},
},
},
expected: "1",
expected: "0",
networks: map[string]*docker.NetworkResource{},
},
{
@ -2034,7 +2055,7 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@ -2122,11 +2143,11 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
"server-test2": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,

View file

@ -17,7 +17,7 @@ type Etcd struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Etcd) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Etcd) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := provider.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)

View file

@ -23,7 +23,7 @@ type Eureka struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Eureka) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error {
func (provider *Eureka) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ types.Constraints) error {
operation := func() error {
configuration, err := provider.buildConfiguration()

View file

@ -21,7 +21,7 @@ type File struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *File) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error {
func (provider *File) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Error("Error creating file watcher", err)

View file

@ -42,7 +42,7 @@ func (provider *Kubernetes) newK8sClient() (k8s.Client, error) {
// 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.Constraint) error {
func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
k8sClient, err := provider.newK8sClient()
if err != nil {
return err

View file

@ -83,7 +83,7 @@ func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix
return nil
}
func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
provider.Constraints = append(provider.Constraints, constraints...)
operation := func() error {
if _, err := provider.kvclient.Exists("qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"); err != nil {

View file

@ -408,7 +408,7 @@ func TestKVLoadConfig(t *testing.T) {
},
{
Key: "traefik/backends/backend.with.dot.too/servers/server.with.dot/weight",
Value: []byte("1"),
Value: []byte("0"),
},
},
},
@ -420,7 +420,7 @@ func TestKVLoadConfig(t *testing.T) {
Servers: map[string]types.Server{
"server.with.dot": {
URL: "http://172.17.0.2:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,

View file

@ -4,7 +4,6 @@ import (
"errors"
"net"
"net/url"
"sort"
"strconv"
"strings"
"text/template"
@ -53,7 +52,7 @@ type lightMarathonClient interface {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
provider.Constraints = append(provider.Constraints, constraints...)
operation := func() error {
config := marathon.NewDefaultConfig()
@ -226,10 +225,7 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon.
log.Debugf("Filtering marathon task %s specifying both traefik.portIndex and traefik.port labels", task.AppID)
return false
}
if portIndexLabel == "" && portValueLabel == "" && len(application.Ports) > 1 {
log.Debugf("Filtering marathon task %s with more than 1 port and no traefik.portIndex or traefik.port label", task.AppID)
return false
}
if portIndexLabel != "" {
index, err := strconv.Atoi((*application.Labels)["traefik.portIndex"])
if err != nil || index < 0 || index > len(application.Ports)-1 {
@ -429,7 +425,7 @@ func (provider *Marathon) getFrontendBackend(application marathon.Application) s
func (provider *Marathon) getSubDomain(name string) string {
if provider.GroupsAsSubDomains {
splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/")
sort.Sort(sort.Reverse(sort.StringSlice(splitedName)))
reverseStringSlice(&splitedName)
reverseName := strings.Join(splitedName, ".")
return reverseName
}

View file

@ -371,30 +371,30 @@ func TestMarathonTaskFilter(t *testing.T) {
},
{
task: marathon.Task{
AppID: "foo",
Ports: []int{80},
AppID: "multiple-ports",
Ports: []int{80, 443},
},
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "foo",
ID: "multiple-ports",
Ports: []int{80, 443},
Labels: &map[string]string{},
},
},
},
expected: false,
expected: true,
exposedByDefault: true,
},
{
task: marathon.Task{
AppID: "foo",
AppID: "disable",
Ports: []int{80},
},
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "foo",
ID: "disable",
Ports: []int{80},
Labels: &map[string]string{
"traefik.enable": "false",
@ -523,7 +523,7 @@ func TestMarathonTaskFilter(t *testing.T) {
},
{
task: marathon.Task{
AppID: "foo",
AppID: "healthcheck-false",
Ports: []int{80},
HealthCheckResults: []*marathon.HealthCheckResult{
{
@ -534,7 +534,7 @@ func TestMarathonTaskFilter(t *testing.T) {
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "foo",
ID: "healthcheck-false",
Ports: []int{80},
Labels: &map[string]string{},
HealthChecks: &[]marathon.HealthCheck{
@ -576,13 +576,13 @@ func TestMarathonTaskFilter(t *testing.T) {
},
{
task: marathon.Task{
AppID: "foo",
AppID: "single-port",
Ports: []int{80},
},
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "foo",
ID: "single-port",
Ports: []int{80},
Labels: &map[string]string{},
},
@ -593,7 +593,7 @@ func TestMarathonTaskFilter(t *testing.T) {
},
{
task: marathon.Task{
AppID: "foo",
AppID: "healthcheck-alive",
Ports: []int{80},
HealthCheckResults: []*marathon.HealthCheckResult{
{
@ -604,7 +604,7 @@ func TestMarathonTaskFilter(t *testing.T) {
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "foo",
ID: "healthcheck-alive",
Ports: []int{80},
Labels: &map[string]string{},
HealthChecks: &[]marathon.HealthCheck{
@ -677,7 +677,7 @@ func TestMarathonTaskFilter(t *testing.T) {
for _, c := range cases {
actual := provider.taskFilter(c.task, c.applications, c.exposedByDefault)
if actual != c.expected {
t.Fatalf("expected %v, got %v", c.expected, actual)
t.Fatalf("App %s: expected %v, got %v", c.task.AppID, c.expected, actual)
}
}
}
@ -740,7 +740,7 @@ func TestMarathonAppConstraints(t *testing.T) {
MarathonLBCompatibility: c.marathonLBCompatibility,
}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
provider.Constraints = types.Constraints{constraint}
actual := provider.applicationFilter(c.application, c.filteredTasks)
if actual != c.expected {
t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.application)
@ -820,7 +820,7 @@ func TestMarathonTaskConstraints(t *testing.T) {
MarathonLBCompatibility: c.marathonLBCompatibility,
}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
provider.Constraints = types.Constraints{constraint}
apps := new(marathon.Applications)
apps.Apps = c.applications
actual := provider.taskFilter(c.filteredTask, apps, true)
@ -927,12 +927,12 @@ func TestMarathonGetPort(t *testing.T) {
{
applications: []marathon.Application{
{
ID: "test1",
ID: "multiple-ports-take-first",
Labels: &map[string]string{},
},
},
task: marathon.Task{
AppID: "test1",
AppID: "multiple-ports-take-first",
Ports: []int{80, 443},
},
expected: "80",
@ -1280,3 +1280,33 @@ func TestMarathonGetBackend(t *testing.T) {
}
}
}
func TestMarathonGetSubDomain(t *testing.T) {
providerGroups := &Marathon{GroupsAsSubDomains: true}
providerNoGroups := &Marathon{GroupsAsSubDomains: false}
apps := []struct {
path string
expected string
provider *Marathon
}{
{"/test", "test", providerNoGroups},
{"/test", "test", providerGroups},
{"/a/b/c/d", "d.c.b.a", providerGroups},
{"/b/a/d/c", "c.d.a.b", providerGroups},
{"/d/c/b/a", "a.b.c.d", providerGroups},
{"/c/d/a/b", "b.a.d.c", providerGroups},
{"/a/b/c/d", "a-b-c-d", providerNoGroups},
{"/b/a/d/c", "b-a-d-c", providerNoGroups},
{"/d/c/b/a", "d-c-b-a", providerNoGroups},
{"/c/d/a/b", "c-d-a-b", providerNoGroups},
}
for _, a := range apps {
actual := a.provider.getSubDomain(a.path)
if actual != a.expected {
t.Errorf("expected %q, got %q", a.expected, actual)
}
}
}

View file

@ -7,6 +7,8 @@ import (
"text/template"
"fmt"
"time"
"github.com/BurntSushi/ty/fun"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
@ -20,8 +22,6 @@ import (
"github.com/mesosphere/mesos-dns/records"
"github.com/mesosphere/mesos-dns/records/state"
"github.com/mesosphere/mesos-dns/util"
"sort"
"time"
)
var _ Provider = (*Mesos)(nil)
@ -42,7 +42,7 @@ type Mesos struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Mesos) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Mesos) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
operation := func() error {
// initialize logging
@ -212,10 +212,6 @@ func mesosTaskFilter(task state.Task, exposedByDefaultFlag bool) bool {
log.Debugf("Filtering mesos task %s specifying both traefik.portIndex and traefik.port labels", task.Name)
return false
}
if portIndexLabel == "" && portValueLabel == "" && len(task.DiscoveryInfo.Ports.DiscoveryPorts) > 1 {
log.Debugf("Filtering mesos task %s with more than 1 port and no traefik.portIndex or traefik.port label", 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 {
@ -439,7 +435,7 @@ func Ignore(f ErrorFunction) {
func (provider *Mesos) getSubDomain(name string) string {
if provider.GroupsAsSubDomains {
splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/")
sort.Sort(sort.Reverse(sort.StringSlice(splitedName)))
reverseStringSlice(&splitedName)
reverseName := strings.Join(splitedName, ".")
return reverseName
}

View file

@ -1,11 +1,12 @@
package provider
import (
"reflect"
"testing"
"github.com/containous/traefik/log"
"github.com/containous/traefik/types"
"github.com/mesosphere/mesos-dns/records/state"
"reflect"
"testing"
)
func TestMesosTaskFilter(t *testing.T) {
@ -95,7 +96,7 @@ func TestMesosTaskFilter(t *testing.T) {
setLabels("traefik.enable", "true"),
discovery(setDiscoveryPorts("TCP", 80, "WEB HTTP", "TCP", 443, "WEB HTTPS")),
),
expected: false, // more than 1 discovery port but no traefik.port* label
expected: true, // Default to first index
exposedByDefault: true,
},
{
@ -244,6 +245,36 @@ func TestMesosLoadConfig(t *testing.T) {
}
}
func TestMesosGetSubDomain(t *testing.T) {
providerGroups := &Mesos{GroupsAsSubDomains: true}
providerNoGroups := &Mesos{GroupsAsSubDomains: false}
apps := []struct {
path string
expected string
provider *Mesos
}{
{"/test", "test", providerNoGroups},
{"/test", "test", providerGroups},
{"/a/b/c/d", "d.c.b.a", providerGroups},
{"/b/a/d/c", "c.d.a.b", providerGroups},
{"/d/c/b/a", "a.b.c.d", providerGroups},
{"/c/d/a/b", "b.a.d.c", providerGroups},
{"/a/b/c/d", "a-b-c-d", providerNoGroups},
{"/b/a/d/c", "b-a-d-c", providerNoGroups},
{"/d/c/b/a", "d-c-b-a", providerNoGroups},
{"/c/d/a/b", "c-d-a-b", providerNoGroups},
}
for _, a := range apps {
actual := a.provider.getSubDomain(a.path)
if actual != a.expected {
t.Errorf("expected %q, got %q", a.expected, actual)
}
}
}
// test helpers
type (

View file

@ -23,7 +23,7 @@ import (
type Provider interface {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error
Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error
}
// BaseProvider should be inherited by providers
@ -44,7 +44,7 @@ func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint)
for _, constraint := range p.Constraints {
// xor: if ok and constraint.MustMatch are equal, then no tag is currently matching with the constraint
if ok := constraint.MatchConstraintWithAtLeastOneTag(tags); ok != constraint.MustMatch {
return false, &constraint
return false, constraint
}
}
@ -101,6 +101,12 @@ func normalize(name string) string {
return strings.Join(strings.FieldsFunc(name, fargs), "-")
}
func reverseStringSlice(slice *[]string) {
for i, j := 0, len(*slice)-1; i < j; i, j = i+1, j-1 {
(*slice)[i], (*slice)[j] = (*slice)[j], (*slice)[i]
}
}
// ClientTLS holds TLS specific configurations as client
// CA, Cert and Key can be either path or file contents
type ClientTLS struct {

View file

@ -230,13 +230,13 @@ func TestNilClientTLS(t *testing.T) {
func TestMatchingConstraints(t *testing.T) {
cases := []struct {
constraints []types.Constraint
constraints types.Constraints
tags []string
expected bool
}{
// simple test: must match
{
constraints: []types.Constraint{
constraints: types.Constraints{
{
Key: "tag",
MustMatch: true,
@ -250,7 +250,7 @@ func TestMatchingConstraints(t *testing.T) {
},
// simple test: must match but does not match
{
constraints: []types.Constraint{
constraints: types.Constraints{
{
Key: "tag",
MustMatch: true,
@ -264,7 +264,7 @@ func TestMatchingConstraints(t *testing.T) {
},
// simple test: must not match
{
constraints: []types.Constraint{
constraints: types.Constraints{
{
Key: "tag",
MustMatch: false,
@ -278,7 +278,7 @@ func TestMatchingConstraints(t *testing.T) {
},
// complex test: globbing
{
constraints: []types.Constraint{
constraints: types.Constraints{
{
Key: "tag",
MustMatch: true,
@ -292,7 +292,7 @@ func TestMatchingConstraints(t *testing.T) {
},
// complex test: multiple constraints
{
constraints: []types.Constraint{
constraints: types.Constraints{
{
Key: "tag",
MustMatch: true,

View file

@ -17,7 +17,7 @@ type Zookepper struct {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Zookepper) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
func (provider *Zookepper) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := provider.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)