mprove Rancher provider functionality:
- Improves default filtering behavior to filter by container health/healthState - Optionally allows filtering by service health/healthState - Allows configuration of refresh interval
This commit is contained in:
parent
e2fdc27d64
commit
44db6e9290
5 changed files with 205 additions and 22 deletions
13
docs/toml.md
13
docs/toml.md
|
@ -1604,6 +1604,12 @@ domain = "rancher.localhost"
|
||||||
#
|
#
|
||||||
Watch = true
|
Watch = true
|
||||||
|
|
||||||
|
# Polling interval (in seconds)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
RefreshSeconds = 15
|
||||||
|
|
||||||
# Expose Rancher services by default in traefik
|
# Expose Rancher services by default in traefik
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
|
@ -1611,6 +1617,13 @@ Watch = true
|
||||||
#
|
#
|
||||||
ExposedByDefault = false
|
ExposedByDefault = false
|
||||||
|
|
||||||
|
# Filter services with unhealthy states and health states
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
EnableServiceHealthFilter = false
|
||||||
|
|
||||||
# Endpoint to use when connecting to Rancher
|
# Endpoint to use when connecting to Rancher
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
|
|
|
@ -21,11 +21,6 @@ import (
|
||||||
rancher "github.com/rancher/go-rancher/client"
|
rancher "github.com/rancher/go-rancher/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// RancherDefaultWatchTime is the duration of the interval when polling rancher
|
|
||||||
RancherDefaultWatchTime = 15 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
withoutPagination *rancher.ListOpts
|
withoutPagination *rancher.ListOpts
|
||||||
)
|
)
|
||||||
|
@ -34,12 +29,14 @@ var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
// Provider holds configurations of the provider.
|
// Provider holds configurations of the provider.
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
provider.BaseProvider `mapstructure:",squash"`
|
provider.BaseProvider `mapstructure:",squash"`
|
||||||
Endpoint string `description:"Rancher server HTTP(S) endpoint."`
|
Endpoint string `description:"Rancher server HTTP(S) endpoint."`
|
||||||
AccessKey string `description:"Rancher server access key."`
|
AccessKey string `description:"Rancher server access key."`
|
||||||
SecretKey string `description:"Rancher server Secret Key."`
|
SecretKey string `description:"Rancher server Secret Key."`
|
||||||
ExposedByDefault bool `description:"Expose Services by default"`
|
ExposedByDefault bool `description:"Expose Services by default"`
|
||||||
Domain string `description:"Default domain used"`
|
Domain string `description:"Default domain used"`
|
||||||
|
RefreshSeconds int `description:"Polling interval (in seconds)"`
|
||||||
|
EnableServiceHealthFilter bool `description:"Filter services with unhealthy states and health states."`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rancherData struct {
|
type rancherData struct {
|
||||||
|
@ -47,6 +44,7 @@ type rancherData struct {
|
||||||
Labels map[string]string // List of labels set to container or service
|
Labels map[string]string // List of labels set to container or service
|
||||||
Containers []string
|
Containers []string
|
||||||
Health string
|
Health string
|
||||||
|
State string
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -56,7 +54,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r rancherData) String() string {
|
func (r rancherData) String() string {
|
||||||
return fmt.Sprintf("{name:%s, labels:%v, containers: %v, health: %s}", r.Name, r.Labels, r.Containers, r.Health)
|
return fmt.Sprintf("{name:%s, labels:%v, containers: %v, health: %s, state: %s}", r.Name, r.Labels, r.Containers, r.Health, r.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frontend Labels
|
// Frontend Labels
|
||||||
|
@ -261,7 +259,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
||||||
|
|
||||||
if p.Watch {
|
if p.Watch {
|
||||||
_, cancel := context.WithCancel(ctx)
|
_, cancel := context.WithCancel(ctx)
|
||||||
ticker := time.NewTicker(RancherDefaultWatchTime)
|
ticker := time.NewTicker(time.Second * time.Duration(p.RefreshSeconds))
|
||||||
pool.Go(func(stop chan bool) {
|
pool.Go(func(stop chan bool) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -384,6 +382,7 @@ func parseRancherData(environments []*rancher.Environment, services []*rancher.S
|
||||||
rancherData := rancherData{
|
rancherData := rancherData{
|
||||||
Name: environment.Name + "/" + service.Name,
|
Name: environment.Name + "/" + service.Name,
|
||||||
Health: service.HealthState,
|
Health: service.HealthState,
|
||||||
|
State: service.State,
|
||||||
Labels: make(map[string]string),
|
Labels: make(map[string]string),
|
||||||
Containers: []string{},
|
Containers: []string{},
|
||||||
}
|
}
|
||||||
|
@ -393,11 +392,8 @@ func parseRancherData(environments []*rancher.Environment, services []*rancher.S
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
for key, value := range container.Labels {
|
if container.Labels["io.rancher.stack_service.name"] == rancherData.Name && containerFilter(container) {
|
||||||
|
rancherData.Containers = append(rancherData.Containers, container.PrimaryIpAddress)
|
||||||
if key == "io.rancher.stack_service.name" && value == rancherData.Name {
|
|
||||||
rancherData.Containers = append(rancherData.Containers, container.PrimaryIpAddress)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rancherDataList = append(rancherDataList, rancherData)
|
rancherDataList = append(rancherDataList, rancherData)
|
||||||
|
@ -463,6 +459,20 @@ func (p *Provider) loadRancherConfig(services []rancherData) *types.Configuratio
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func containerFilter(container *rancher.Container) bool {
|
||||||
|
if container.HealthState != "" && container.HealthState != "healthy" && container.HealthState != "updating-healthy" {
|
||||||
|
log.Debugf("Filtering container %s with healthState of %s", container.Name, container.HealthState)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if container.State != "" && container.State != "running" && container.State != "updating-running" {
|
||||||
|
log.Debugf("Filtering container %s with state of %s", container.Name, container.State)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) serviceFilter(service rancherData) bool {
|
func (p *Provider) serviceFilter(service rancherData) bool {
|
||||||
|
|
||||||
if service.Labels["traefik.port"] == "" {
|
if service.Labels["traefik.port"] == "" {
|
||||||
|
@ -475,9 +485,18 @@ func (p *Provider) serviceFilter(service rancherData) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if service.Health != "" && service.Health != "healthy" {
|
// Only filter services by Health (HealthState) and State if EnableServiceHealthFilter is true
|
||||||
log.Debugf("Filtering unhealthy or starting service %s", service.Name)
|
if p.EnableServiceHealthFilter {
|
||||||
return false
|
|
||||||
|
if service.Health != "" && service.Health != "healthy" && service.Health != "updating-healthy" {
|
||||||
|
log.Debugf("Filtering service %s with healthState of %s", service.Name, service.Health)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.State != "" && service.State != "active" && service.State != "updating-active" && service.State != "upgraded" {
|
||||||
|
log.Debugf("Filtering service %s with state of %s", service.Name, service.State)
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,12 +1,148 @@
|
||||||
package rancher
|
package rancher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
rancher "github.com/rancher/go-rancher/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestRancherServiceFilter(t *testing.T) {
|
||||||
|
provider := &Provider{
|
||||||
|
Domain: "rancher.localhost",
|
||||||
|
EnableServiceHealthFilter: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
services := []struct {
|
||||||
|
service rancherData
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"traefik.enable": "true",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"traefik.port": "80",
|
||||||
|
"traefik.enable": "false",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"traefik.port": "80",
|
||||||
|
"traefik.enable": "true",
|
||||||
|
},
|
||||||
|
Health: "unhealthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"traefik.port": "80",
|
||||||
|
"traefik.enable": "true",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "inactive",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"traefik.port": "80",
|
||||||
|
"traefik.enable": "true",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"traefik.port": "80",
|
||||||
|
"traefik.enable": "true",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "upgraded",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range services {
|
||||||
|
actual := provider.serviceFilter(e.service)
|
||||||
|
if actual != e.expected {
|
||||||
|
t.Fatalf("expected %t, got %t", e.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRancherContainerFilter(t *testing.T) {
|
||||||
|
containers := []struct {
|
||||||
|
container *rancher.Container
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
container: &rancher.Container{
|
||||||
|
HealthState: "unhealthy",
|
||||||
|
State: "running",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: &rancher.Container{
|
||||||
|
HealthState: "healthy",
|
||||||
|
State: "stopped",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: &rancher.Container{
|
||||||
|
State: "stopped",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: &rancher.Container{
|
||||||
|
HealthState: "healthy",
|
||||||
|
State: "running",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: &rancher.Container{
|
||||||
|
HealthState: "updating-healthy",
|
||||||
|
State: "updating-running",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range containers {
|
||||||
|
actual := containerFilter(e.container)
|
||||||
|
if actual != e.expected {
|
||||||
|
t.Fatalf("expected %t, got %t", e.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRancherGetFrontendName(t *testing.T) {
|
func TestRancherGetFrontendName(t *testing.T) {
|
||||||
provider := &Provider{
|
provider := &Provider{
|
||||||
Domain: "rancher.localhost",
|
Domain: "rancher.localhost",
|
||||||
|
|
|
@ -446,6 +446,8 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||||
var defaultRancher rancher.Provider
|
var defaultRancher rancher.Provider
|
||||||
defaultRancher.Watch = true
|
defaultRancher.Watch = true
|
||||||
defaultRancher.ExposedByDefault = true
|
defaultRancher.ExposedByDefault = true
|
||||||
|
defaultRancher.RefreshSeconds = 15
|
||||||
|
defaultRancher.EnableServiceHealthFilter = false
|
||||||
|
|
||||||
// default DynamoDB
|
// default DynamoDB
|
||||||
var defaultDynamoDB dynamodb.Provider
|
var defaultDynamoDB dynamodb.Provider
|
||||||
|
|
|
@ -1046,6 +1046,12 @@
|
||||||
#
|
#
|
||||||
# Watch = true
|
# Watch = true
|
||||||
|
|
||||||
|
# Polling interval (in seconds)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# RefreshSeconds = 15
|
||||||
|
|
||||||
# Expose Rancher services by default in traefik
|
# Expose Rancher services by default in traefik
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
|
@ -1053,6 +1059,13 @@
|
||||||
#
|
#
|
||||||
# ExposedByDefault = false
|
# ExposedByDefault = false
|
||||||
|
|
||||||
|
# Filter services with unhealthy states and health states
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# EnableServiceHealthFilter = false
|
||||||
|
|
||||||
# Endpoint to use when connecting to Rancher
|
# Endpoint to use when connecting to Rancher
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
|
|
Loading…
Reference in a new issue