diff --git a/main.go b/main.go index 2222b38..9075cf0 100644 --- a/main.go +++ b/main.go @@ -6,18 +6,18 @@ import ( "os" "os/signal" "sort" - "strings" "sync" "syscall" "time" "github.com/cenkalti/backoff" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/szuecs/kube-static-egress-controller/kube" - provider "github.com/szuecs/kube-static-egress-controller/provider" + "github.com/szuecs/kube-static-egress-controller/provider" + "github.com/szuecs/kube-static-egress-controller/provider/aws" + "github.com/szuecs/kube-static-egress-controller/provider/noop" kingpin "gopkg.in/alecthomas/kingpin.v2" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" @@ -64,6 +64,18 @@ func NewConfig() *Config { return &Config{} } +func newProvider(dry bool, name string, natCidrBlocks, availabilityZones []string, StackTerminationProtection bool) provider.Provider { + switch name { + case aws.ProviderName: + return aws.NewAwsProvider(dry, natCidrBlocks, availabilityZones, StackTerminationProtection) + case noop.ProviderName: + return noop.NewNoopProvider() + default: + log.Fatalf("Unkown provider: %s", name) + } + return nil +} + func allLogLevelsAsStrings() []string { var levels []string for _, level := range log.AllLevels { @@ -125,7 +137,7 @@ func main() { log.SetLevel(ll) log.Debugf("config: %+v", cfg) - p := provider.NewProvider(cfg.DryRun, cfg.Provider, cfg.NatCidrBlocks, cfg.AvailabilityZones, cfg.StackTerminationProtection) + p := newProvider(cfg.DryRun, cfg.Provider, cfg.NatCidrBlocks, cfg.AvailabilityZones, cfg.StackTerminationProtection) run(newKubeClient(), p) } @@ -270,8 +282,16 @@ func enterProvider(wg *sync.WaitGroup, p provider.Provider, mergerCH <-chan []st bootstrap = false continue } - var err error + createNotify := func(err error, t time.Duration) { + switch errors.Cause(err).(type) { + case *provider.AlreadyExistsError: + err = p.Update(output) + if err != nil { + log.Error(err) + } + } + } if len(input) == 0 { // not caused by faulty value in CIDR string if !sameValues(resultCache, output) { resultCache = output @@ -287,7 +307,7 @@ func enterProvider(wg *sync.WaitGroup, p provider.Provider, mergerCH <-chan []st createFunc := func() error { return p.Create(output) } - err = backoff.Retry(createFunc, retry) + err = backoff.RetryNotify(createFunc, retry, createNotify) if err != nil { log.Error(err) } @@ -295,19 +315,17 @@ func enterProvider(wg *sync.WaitGroup, p provider.Provider, mergerCH <-chan []st err = p.Update(output) // create if stack does not exist, but we have targets if err != nil { - switch e := errors.Cause(err).(type) { - case awserr.Error: - log.Infof("%s | %s | %s", e.Code(), e.Message(), e.OrigErr()) - if strings.Contains(e.Message(), "does not exist") { - err = p.Create(output) - createFunc := func() error { - return p.Create(output) - } - err = backoff.Retry(createFunc, retry) - if err != nil { - log.Error(err) - } + switch errors.Cause(err).(type) { + case *provider.DoesNotExistError: + createFunc := func() error { + return p.Create(output) + } + err = backoff.RetryNotify(createFunc, retry, createNotify) + if err != nil { + log.Error(err) } + default: + log.Errorf("Failed to update stack: %v", err) } } } diff --git a/main_test.go b/main_test.go index 18717a3..0f988d9 100644 --- a/main_test.go +++ b/main_test.go @@ -169,7 +169,7 @@ func Test_enterProvider(t *testing.T) { { name: "enterProvider noop quit test", wg: sync.WaitGroup{}, - p: provider.NewProvider(true, noop.ProviderName, []string{}, []string{}, false), + p: newProvider(true, noop.ProviderName, []string{}, []string{}, false), mergerCH: make(chan []string), quitCH: make(chan struct{}), timeout: 3 * time.Second, diff --git a/provider/aws/aws.go b/provider/aws/aws.go index 9d1dd00..14a08fe 100644 --- a/provider/aws/aws.go +++ b/provider/aws/aws.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudformation" @@ -16,6 +17,7 @@ import ( "github.com/linki/instrumented_http" "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/szuecs/kube-static-egress-controller/provider" ) const ( @@ -304,6 +306,11 @@ func (p *AwsProvider) updateCFStack(nets []string, spec *stackSpec) (string, err resp, err := p.cloudformation.UpdateStack(params) if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "AlreadyExistsException" { + err = provider.NewAlreadyExistsError(fmt.Sprintf("%s AlreadyExists", stackName)) + } + } return spec.name, err } return aws.StringValue(resp.StackId), nil @@ -335,6 +342,11 @@ func (p *AwsProvider) createCFStack(nets []string, spec *stackSpec) (string, err resp, err := p.cloudformation.CreateStack(params) log.Debugf("Stackoutput: %+v", resp) if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + if strings.Contains(awsErr.Message(), "does not exist") { + err = provider.NewDoesNotExistError(fmt.Sprintf("%s does not exist", stackName)) + } + } return spec.name, err } return aws.StringValue(resp.StackId), nil diff --git a/provider/error.go b/provider/error.go new file mode 100644 index 0000000..867b31a --- /dev/null +++ b/provider/error.go @@ -0,0 +1,25 @@ +package provider + +type AlreadyExistsError struct { + msg string +} + +type DoesNotExistError struct { + msg string +} + +func (error *AlreadyExistsError) Error() string { + return error.msg +} + +func (error *DoesNotExistError) Error() string { + return error.msg +} + +func NewAlreadyExistsError(msg string) error { + return &AlreadyExistsError{msg} +} + +func NewDoesNotExistError(msg string) error { + return &DoesNotExistError{msg} +} diff --git a/provider/provider.go b/provider/provider.go index 8c751f1..df5edfb 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -1,26 +1,8 @@ package provider -import ( - log "github.com/sirupsen/logrus" - "github.com/szuecs/kube-static-egress-controller/provider/aws" - "github.com/szuecs/kube-static-egress-controller/provider/noop" -) - type Provider interface { Create([]string) error Update([]string) error Delete() error String() string } - -func NewProvider(dry bool, name string, natCidrBlocks, availabilityZones []string, StackTerminationProtection bool) Provider { - switch name { - case aws.ProviderName: - return aws.NewAwsProvider(dry, natCidrBlocks, availabilityZones, StackTerminationProtection) - case noop.ProviderName: - return noop.NewNoopProvider() - default: - log.Fatalf("Unkown provider: %s", name) - } - return nil -}