Skip to content

Commit

Permalink
Update main
Browse files Browse the repository at this point in the history
* Fix: Add only healthy containers to the target pool
* Fix: Only attempt to update provider configs if the LB config from
metadata has changed. This prevents flooding of the provider API when
there is a flapping service in the environment that causes the metadata
version to change rapidly.
* Enhancement: Set Cattle service FQDN to the FQDN of the load balancer
(if supported by the provider)
* Refactored code
  • Loading branch information
janeczku committed Sep 23, 2016
1 parent d7f6048 commit 6fb5064
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 232 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
external-lb
68 changes: 68 additions & 0 deletions cattle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"fmt"
"os"

"github.com/rancher/go-rancher/client"
)

type CattleClient struct {
rancherClient *client.RancherClient
}

func NewCattleClientFromEnvironment() (*CattleClient, error) {
var cattleURL string
var cattleAccessKey string
var cattleSecretKey string

if env := os.Getenv("CATTLE_URL"); len(env) > 0 {
cattleURL = env
} else {
return nil, fmt.Errorf("Environment variable 'CATTLE_URL' is not set")
}

if env := os.Getenv("CATTLE_ACCESS_KEY"); len(env) > 0 {
cattleAccessKey = env
} else {
return nil, fmt.Errorf("Environment variable 'CATTLE_ACCESS_KEY' is not set")
}

if env := os.Getenv("CATTLE_SECRET_KEY"); len(env) > 0 {
cattleSecretKey = env
} else {
return nil, fmt.Errorf("Environment variable 'CATTLE_SECRET_KEY' is not set")
}

apiClient, err := client.NewRancherClient(&client.ClientOpts{
Url: cattleURL,
AccessKey: cattleAccessKey,
SecretKey: cattleSecretKey,
})

if err != nil {
return nil, err
}

return &CattleClient{
rancherClient: apiClient,
}, nil
}

func (c *CattleClient) UpdateServiceFqdn(serviceName, stackName, fqdn string) error {
event := &client.ExternalDnsEvent{
EventType: "dns.update",
ExternalId: fqdn,
ServiceName: serviceName,
StackName: stackName,
Fqdn: fqdn,
}
_, err := c.rancherClient.ExternalDnsEvent.Create(event)
return err
}

func (c *CattleClient) TestConnect() error {
opts := &client.ListOpts{}
_, err := c.rancherClient.ExternalDnsEvent.List(opts)
return err
}
114 changes: 67 additions & 47 deletions external-lb.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,51 @@ import (
"strings"
)

func UpdateProviderLBConfigs(metadataConfigs map[string]model.LBConfig) error {
type Op int

const (
ADD Op = iota
REMOVE
UPDATE
)

func UpdateProviderLBConfigs(metadataConfigs map[string]model.LBConfig) (map[string]model.LBConfig, error) {
providerConfigs, err := getProviderLBConfigs()
if err != nil {
return fmt.Errorf("Provider error reading lb configs: %v", err)
return nil, fmt.Errorf("Failed to get LB configs from provider: %v", err)
}
logrus.Debugf("Rancher LB configs from provider: %v", providerConfigs)

removeExtraConfigs(metadataConfigs, providerConfigs)
updated := addMissingConfigs(metadataConfigs, providerConfigs)
updated_ := updateExistingConfigs(metadataConfigs, providerConfigs)
for k, v := range updated_ {
if _, ok := updated[k]; !ok {
updated[k] = v
}
}

addMissingConfigs(metadataConfigs, providerConfigs)

updateExistingConfigs(metadataConfigs, providerConfigs)

return nil
return updated, nil
}

func getProviderLBConfigs() (map[string]model.LBConfig, error) {
allConfigs, err := provider.GetLBConfigs()
if err != nil {
logrus.Debugf("Error Getting Rancher LB configs from provider: %v", err)
return nil, err
}

rancherConfigs := make(map[string]model.LBConfig, len(allConfigs))
suffix := "_" + m.EnvironmentUUID + "_" + targetRancherSuffix
suffix := "_" + m.EnvironmentUUID + "_" + targetPoolSuffix
for _, value := range allConfigs {
if strings.HasSuffix(value.LBTargetPoolName, suffix) {
rancherConfigs[value.LBEndpoint] = value
}
}

logrus.Debugf("LBConfigs from provider: %v", allConfigs)
return rancherConfigs, nil
}

func removeExtraConfigs(metadataConfigs map[string]model.LBConfig, providerConfigs map[string]model.LBConfig) []model.LBConfig {
func removeExtraConfigs(metadataConfigs, providerConfigs map[string]model.LBConfig) map[string]model.LBConfig {
var toRemove []model.LBConfig
for key := range providerConfigs {
if _, ok := metadataConfigs[key]; !ok {
Expand All @@ -50,27 +62,30 @@ func removeExtraConfigs(metadataConfigs map[string]model.LBConfig, providerConfi
if len(toRemove) == 0 {
logrus.Debug("No LB configs to remove")
} else {
logrus.Infof("LB configs to remove: %v", toRemove)
logrus.Infof("LB configs to remove: %d", len(toRemove))
}
return updateProvider(toRemove, &Remove)

return updateProvider(toRemove, REMOVE)
}

func addMissingConfigs(metadataConfigs map[string]model.LBConfig, providerConfigs map[string]model.LBConfig) []model.LBConfig {
func addMissingConfigs(metadataConfigs, providerConfigs map[string]model.LBConfig) map[string]model.LBConfig {
var toAdd []model.LBConfig
for key := range metadataConfigs {
if _, ok := providerConfigs[key]; !ok {
toAdd = append(toAdd, metadataConfigs[key])
}
}

if len(toAdd) == 0 {
logrus.Debug("No LB configs to add")
} else {
logrus.Infof("LB configs to add: %v", toAdd)
logrus.Infof("LB configs to add: %d", len(toAdd))
}
return updateProvider(toAdd, &Add)

return updateProvider(toAdd, ADD)
}

func updateExistingConfigs(metadataConfigs map[string]model.LBConfig, providerConfigs map[string]model.LBConfig) []model.LBConfig {
func updateExistingConfigs(metadataConfigs, providerConfigs map[string]model.LBConfig) map[string]model.LBConfig {
var toUpdate []model.LBConfig
for key := range metadataConfigs {
if _, ok := providerConfigs[key]; ok {
Expand All @@ -82,21 +97,22 @@ func updateExistingConfigs(metadataConfigs map[string]model.LBConfig, providerCo
if strings.EqualFold(mLBConfig.LBTargetPoolName, pLBConfig.LBTargetPoolName) {
if len(mLBConfig.LBTargets) != len(pLBConfig.LBTargets) {
update = true
}
//check if any target have changed
for _, mTarget := range mLBConfig.LBTargets {
targetExists := false
for _, pTarget := range pLBConfig.LBTargets {
if pTarget.HostIP == mTarget.HostIP && pTarget.Port == mTarget.Port {
targetExists = true
} else {
//check if any target have changed
for _, mTarget := range mLBConfig.LBTargets {
targetExists := false
for _, pTarget := range pLBConfig.LBTargets {
if pTarget.HostIP == mTarget.HostIP && pTarget.Port == mTarget.Port {
targetExists = true
break
}
}
if !targetExists {
//lb target changed, update the config on provider
update = true
break
}
}
if !targetExists {
//lb target changed, update the config on provider
update = true
break
}
}
} else {
//targetPool should be changed
Expand All @@ -113,36 +129,40 @@ func updateExistingConfigs(metadataConfigs map[string]model.LBConfig, providerCo
if len(toUpdate) == 0 {
logrus.Debug("No LB configs to update")
} else {
logrus.Infof("LB configs to update: %v", toUpdate)
logrus.Infof("LB configs to update: %d", len(toUpdate))
}

return updateProvider(toUpdate, &Update)
return updateProvider(toUpdate, UPDATE)
}

func updateProvider(toChange []model.LBConfig, op *Op) []model.LBConfig {
var changed []model.LBConfig
func updateProvider(toChange []model.LBConfig, op Op) map[string]model.LBConfig {
// map of FQDN -> LBConfig
updateFqdn := make(map[string]model.LBConfig)
for _, value := range toChange {
switch *op {
case Add:
switch op {
case ADD:
logrus.Infof("Adding LB config: %v", value)
if err := provider.AddLBConfig(value); err != nil {
logrus.Errorf("Failed to add LB config to provider %v: %v", value, err)
} else {
changed = append(changed, value)
fqdn, err := provider.AddLBConfig(value)
if err != nil {
logrus.Errorf("Failed to add LB config for endpoint %s: %v", value.LBEndpoint, err)
} else if fqdn != "" {
updateFqdn[fqdn] = value
}
case Remove:
case REMOVE:
logrus.Infof("Removing LB config: %v", value)
if err := provider.RemoveLBConfig(value); err != nil {
logrus.Errorf("Failed to remove LB config from provider %v: %v", value, err)
logrus.Errorf("Failed to remove LB config for endpoint %s: %v", value.LBEndpoint, err)
}
case Update:
case UPDATE:
logrus.Infof("Updating LB config: %v", value)
if err := provider.UpdateLBConfig(value); err != nil {
logrus.Errorf("Failed to update LB config to provider %v: %v", value, err)
} else {
changed = append(changed, value)
fqdn, err := provider.UpdateLBConfig(value)
if err != nil {
logrus.Errorf("Failed to update LB config for endpoint %s: %v", value.LBEndpoint, err)
} else if fqdn != "" {
updateFqdn[fqdn] = value
}
}
}
return changed

return updateFqdn
}
9 changes: 4 additions & 5 deletions healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ func healthcheck(w http.ResponseWriter, req *http.Request) {
// 1) test metadata server
_, err := m.MetadataClient.GetSelfStack()
if err != nil {
logrus.Error("Healthcheck failed: unable to reach metadata")
logrus.Error("Metadata health check failed: %v", err)
http.Error(w, "Failed to reach metadata server", http.StatusInternalServerError)
} else {
// 2) test provider
err := provider.TestConnection()
if err != nil {
logrus.Errorf("Healthcheck failed: unable to reach a provider, error:%v", err)
http.Error(w, "Failed to reach an external provider ", http.StatusInternalServerError)
if err := provider.HealthCheck(); err != nil {
logrus.Errorf("Provider health check failed: %v", err)
http.Error(w, "Failed to reach external provider ", http.StatusInternalServerError)
} else {
w.Write([]byte("OK"))
}
Expand Down
Loading

0 comments on commit 6fb5064

Please sign in to comment.