Exposed by default feature in Consul Catalog
This commit is contained in:
parent
e0af17a17a
commit
f16219f90a
8 changed files with 241 additions and 24 deletions
|
@ -31,6 +31,7 @@ type CatalogProvider struct {
|
|||
provider.BaseProvider `mapstructure:",squash"`
|
||||
Endpoint string `description:"Consul server endpoint"`
|
||||
Domain string `description:"Default domain used"`
|
||||
ExposedByDefault bool `description:"Expose Consul services by default"`
|
||||
Prefix string `description:"Prefix used for Consul catalog tags"`
|
||||
FrontEndRule string `description:"Frontend rule used for Consul services"`
|
||||
client *api.Client
|
||||
|
@ -209,12 +210,7 @@ func (p *CatalogProvider) healthyNodes(service string) (catalogUpdate, error) {
|
|||
}
|
||||
|
||||
nodes := fun.Filter(func(node *api.ServiceEntry) bool {
|
||||
constraintTags := p.getConstraintTags(node.Service.Tags)
|
||||
ok, failingConstraint := p.MatchConstraints(constraintTags)
|
||||
if !ok && failingConstraint != nil {
|
||||
log.Debugf("Service %v pruned by '%v' constraint", service, failingConstraint.String())
|
||||
}
|
||||
return ok
|
||||
return p.nodeFilter(service, node)
|
||||
}, data).([]*api.ServiceEntry)
|
||||
|
||||
//Merge tags of nodes matching constraints, in a single slice.
|
||||
|
@ -234,6 +230,32 @@ func (p *CatalogProvider) healthyNodes(service string) (catalogUpdate, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (p *CatalogProvider) nodeFilter(service string, node *api.ServiceEntry) bool {
|
||||
// Filter disabled application.
|
||||
if !p.isServiceEnabled(node) {
|
||||
log.Debugf("Filtering disabled Consul service %s", service)
|
||||
return false
|
||||
}
|
||||
|
||||
// Filter by constraints.
|
||||
constraintTags := p.getConstraintTags(node.Service.Tags)
|
||||
ok, failingConstraint := p.MatchConstraints(constraintTags)
|
||||
if !ok && failingConstraint != nil {
|
||||
log.Debugf("Service %v pruned by '%v' constraint", service, failingConstraint.String())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *CatalogProvider) isServiceEnabled(node *api.ServiceEntry) bool {
|
||||
enable, err := strconv.ParseBool(p.getAttribute("enable", node.Service.Tags, strconv.FormatBool(p.ExposedByDefault)))
|
||||
if err != nil {
|
||||
log.Debugf("Invalid value for enable, set to %b", p.ExposedByDefault)
|
||||
return p.ExposedByDefault
|
||||
}
|
||||
return enable
|
||||
}
|
||||
|
||||
func (p *CatalogProvider) getPrefixedName(name string) string {
|
||||
if len(p.Prefix) > 0 {
|
||||
return p.Prefix + "." + name
|
||||
|
@ -364,14 +386,9 @@ func (p *CatalogProvider) buildConfig(catalog []catalogUpdate) *types.Configurat
|
|||
allNodes := []*api.ServiceEntry{}
|
||||
services := []*serviceUpdate{}
|
||||
for _, info := range catalog {
|
||||
for _, node := range info.Nodes {
|
||||
isEnabled := p.getAttribute("enable", node.Service.Tags, "true")
|
||||
if isEnabled != "false" && len(info.Nodes) > 0 {
|
||||
services = append(services, info.Service)
|
||||
allNodes = append(allNodes, info.Nodes...)
|
||||
break
|
||||
}
|
||||
|
||||
if len(info.Nodes) > 0 {
|
||||
services = append(services, info.Service)
|
||||
allNodes = append(allNodes, info.Nodes...)
|
||||
}
|
||||
}
|
||||
// Ensure a stable ordering of nodes so that identical configurations may be detected
|
||||
|
|
|
@ -311,6 +311,7 @@ func TestConsulCatalogBuildConfig(t *testing.T) {
|
|||
provider := &CatalogProvider{
|
||||
Domain: "localhost",
|
||||
Prefix: "traefik",
|
||||
ExposedByDefault: false,
|
||||
FrontEndRule: "Host:{{.ServiceName}}.{{.Domain}}",
|
||||
frontEndRuleTemplate: template.New("consul catalog frontend rule"),
|
||||
}
|
||||
|
@ -330,7 +331,6 @@ func TestConsulCatalogBuildConfig(t *testing.T) {
|
|||
{
|
||||
Service: &serviceUpdate{
|
||||
ServiceName: "test",
|
||||
Attributes: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -750,3 +750,106 @@ func TestConsulCatalogGetChangedKeys(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsulCatalogFilterEnabled(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
exposedByDefault bool
|
||||
node *api.ServiceEntry
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "exposed",
|
||||
exposedByDefault: true,
|
||||
node: &api.ServiceEntry{
|
||||
Service: &api.AgentService{
|
||||
Service: "api",
|
||||
Address: "10.0.0.1",
|
||||
Port: 80,
|
||||
Tags: []string{""},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "exposed and tolerated by valid label value",
|
||||
exposedByDefault: true,
|
||||
node: &api.ServiceEntry{
|
||||
Service: &api.AgentService{
|
||||
Service: "api",
|
||||
Address: "10.0.0.1",
|
||||
Port: 80,
|
||||
Tags: []string{"", "traefik.enable=true"},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "exposed and tolerated by invalid label value",
|
||||
exposedByDefault: true,
|
||||
node: &api.ServiceEntry{
|
||||
Service: &api.AgentService{
|
||||
Service: "api",
|
||||
Address: "10.0.0.1",
|
||||
Port: 80,
|
||||
Tags: []string{"", "traefik.enable=bad"},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "exposed but overridden by label",
|
||||
exposedByDefault: true,
|
||||
node: &api.ServiceEntry{
|
||||
Service: &api.AgentService{
|
||||
Service: "api",
|
||||
Address: "10.0.0.1",
|
||||
Port: 80,
|
||||
Tags: []string{"", "traefik.enable=false"},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "non-exposed",
|
||||
exposedByDefault: false,
|
||||
node: &api.ServiceEntry{
|
||||
Service: &api.AgentService{
|
||||
Service: "api",
|
||||
Address: "10.0.0.1",
|
||||
Port: 80,
|
||||
Tags: []string{""},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "non-exposed but overridden by label",
|
||||
exposedByDefault: false,
|
||||
node: &api.ServiceEntry{
|
||||
Service: &api.AgentService{
|
||||
Service: "api",
|
||||
Address: "10.0.0.1",
|
||||
Port: 80,
|
||||
Tags: []string{"", "traefik.enable=true"},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
provider := &CatalogProvider{
|
||||
Domain: "localhost",
|
||||
Prefix: "traefik",
|
||||
ExposedByDefault: c.exposedByDefault,
|
||||
}
|
||||
if provider.nodeFilter("test", c.node) != c.expected {
|
||||
t.Errorf("got unexpected filtering = %t", !c.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue