Skip to content

Commit

Permalink
Add finalizer to the user secret and configmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
vpnachev committed Dec 11, 2020
1 parent b88f28d commit baba72a
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .ci/pipeline_definitions
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ terraformer:
target: terraformer
steps:
verify:
image: 'eu.gcr.io/gardener-project/3rd/golang:1.15.3'
image: 'eu.gcr.io/gardener-project/3rd/golang:1.15.5'
jobs:
head-update:
traits:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0

############# golang-base #############
FROM eu.gcr.io/gardener-project/3rd/golang:1.15.3 AS golang-base
FROM eu.gcr.io/gardener-project/3rd/golang:1.15.5 AS golang-base

############# base #############
FROM golang-base AS base
Expand Down
1 change: 1 addition & 0 deletions pkg/terraformer/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ var _ = Describe("Terraformer Config", func() {
})

AfterEach(func() {
testutils.CleanupTestObjects(ctx, testObjs)
testutils.RunCleanupActions()
})

Expand Down
1 change: 1 addition & 0 deletions pkg/terraformer/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var _ = Describe("Terraformer State", func() {

AfterEach(func() {
ctrl.Finish()
testutils.CleanupTestObjects(ctx, testObjs)
testutils.RunCleanupActions()
})

Expand Down
86 changes: 82 additions & 4 deletions pkg/terraformer/terraformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,28 @@ import (
"syscall"
"time"

"github.com/gardener/terraformer/pkg/utils"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
runtimelog "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/gardener/terraformer/pkg/utils"
)

var (
// TerraformBinary is the name of the terraform binary, it allows to overwrite it for testing purposes
TerraformBinary = "terraform"

// allow redirecting output in tests
Stdout, Stderr io.Writer = os.Stdout, os.Stderr
// Stdout alias to os.Stdout allowing output redirection in tests
Stdout io.Writer = os.Stdout

// Stderr alias to os.Stderr allowing output redirection in tests
Stderr io.Writer = os.Stderr

// SignalNotify allows mocking signal.Notify in tests
SignalNotify = signal.Notify
Expand Down Expand Up @@ -128,6 +135,10 @@ func (t *Terraformer) execute(command Command) (rErr error) {
// stop file watcher and wait for it to be finished
defer shutdownFileWatcher()

if err := t.addFinalizer(ctx); err != nil {
return err
}

// initialize terraform plugins
if err := t.executeTerraform(ctx, Init); err != nil {
return fmt.Errorf("error executing terraform %s: %w", Init, err)
Expand All @@ -144,6 +155,11 @@ func (t *Terraformer) execute(command Command) (rErr error) {
}
}

// after a successful execution of destroy command, remove the finalizers from the resources
if command == Destroy {
return t.removeFinalizer(ctx)
}

return nil
}

Expand Down Expand Up @@ -205,3 +221,65 @@ func (t *Terraformer) executeTerraform(ctx context.Context, command Command) err
log.Info("terraform process finished successfully", "command", command)
return nil
}

func (t *Terraformer) addFinalizer(ctx context.Context) error {
logger := t.log.WithName("add-finalizer")
return t.updateObjectFinalizers(ctx, logger, controllerutil.AddFinalizer)

}

func (t *Terraformer) removeFinalizer(ctx context.Context) error {
logger := t.log.WithName("remove-finalizer")
return t.updateObjectFinalizers(ctx, logger, controllerutil.RemoveFinalizer)
}

func (t *Terraformer) updateObjectFinalizers(ctx context.Context, log logr.Logger, patchObj func(obj controllerutil.Object, finalizerName string)) error {
objects := []controllerutil.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: t.config.Namespace,
Name: t.config.VariablesSecretName,
},
},
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: t.config.Namespace,
Name: t.config.ConfigurationConfigMapName,
},
},
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: t.config.Namespace,
Name: t.config.StateConfigMapName,
},
},
}
log.V(3).Info("updating finalizers for terraform resources", "namespace", t.config.Namespace)

for _, obj := range objects {
key, err := client.ObjectKeyFromObject(obj)
if err != nil {
log.Error(err, "failed to construct key", "key", obj, "error")
return err
}

err = t.client.Get(ctx, key, obj)
if err != nil {
if apierrors.IsNotFound(err) {
log.V(1).Info("skipping non-existing object", "key", obj)
continue
}
log.Error(err, "failed to get object", "key", obj, "error")
return err
}

old := obj.DeepCopyObject()
patchObj(obj, TerraformerFinalizer)
if err := t.client.Patch(ctx, obj, client.MergeFrom(old)); client.IgnoreNotFound(err) != nil {
log.Error(err, "failed to update object in the store", "key", obj)
return err
}
}
log.V(3).Info("successfully updated finalizers for terraform resources", "namespace", t.config.Namespace)
return nil
}
17 changes: 13 additions & 4 deletions pkg/terraformer/terraformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ import (
"sync"
"syscall"

"github.com/gardener/terraformer/pkg/terraformer"
"github.com/gardener/terraformer/pkg/utils"
testutils "github.com/gardener/terraformer/test/utils"

"github.com/gardener/gardener/pkg/utils/test"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"k8s.io/apimachinery/pkg/util/clock"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

"github.com/gardener/terraformer/pkg/terraformer"
"github.com/gardener/terraformer/pkg/utils"
testutils "github.com/gardener/terraformer/test/utils"
)

var _ = Describe("Terraformer", func() {
Expand Down Expand Up @@ -83,6 +83,7 @@ var _ = Describe("Terraformer", func() {

AfterEach(func() {
resetVars()
testutils.CleanupTestObjects(ctx, testObjs)
})

Context("basic tests without terraform binary", func() {
Expand Down Expand Up @@ -125,11 +126,19 @@ var _ = Describe("Terraformer", func() {
Expect(tf.Run(terraformer.Apply)).To(Succeed())
Eventually(logBuffer).Should(gbytes.Say("some terraform output"))
Eventually(logBuffer).Should(gbytes.Say("terraform process finished successfully"))
testObjs.Refresh()
Expect(testObjs.ConfigurationConfigMap.Finalizers).To(ContainElement(terraformer.TerraformerFinalizer))
Expect(testObjs.StateConfigMap.Finalizers).To(ContainElement(terraformer.TerraformerFinalizer))
Expect(testObjs.VariablesSecret.Finalizers).To(ContainElement(terraformer.TerraformerFinalizer))
})
It("should run Destroy successfully", func() {
Expect(tf.Run(terraformer.Destroy)).To(Succeed())
Eventually(logBuffer).Should(gbytes.Say("some terraform output"))
Eventually(logBuffer).Should(gbytes.Say("terraform process finished successfully"))
testObjs.Refresh()
Expect(testObjs.ConfigurationConfigMap.Finalizers).ToNot(ContainElement(terraformer.TerraformerFinalizer))
Expect(testObjs.StateConfigMap.Finalizers).ToNot(ContainElement(terraformer.TerraformerFinalizer))
Expect(testObjs.VariablesSecret.Finalizers).ToNot(ContainElement(terraformer.TerraformerFinalizer))
})
It("should run Validate successfully", func() {
Expect(tf.Run(terraformer.Validate)).To(Succeed())
Expand Down
2 changes: 2 additions & 0 deletions pkg/terraformer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const (
Validate Command = "validate"
// Plan is the terraform `plan` command.
Plan Command = "plan"
// TerraformerFinalizer is the finalizer used by the terraformer on the terraform configmaps and secrets
TerraformerFinalizer = "terraformer.gardener.cloud/state"
)

// SupportedCommands contains the set of supported terraform commands, that can be run as `terraformer <command>`.
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/binary/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ var _ = Describe("terraformer", func() {
)
})

AfterEach(func() {
testutils.CleanupTestObjects(ctx, testObjs)
})

Describe("flag validation", func() {
It("should fail, if no command is given", func() {
cmd := exec.Command(pathToTerraformer, args...)
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/pod/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ var _ = Describe("Pod E2E test", func() {
log.Info("using namespace")
})

AfterEach(func() {
testutils.CleanupTestObjects(ctx, testObjs)
})

It("should apply and destroy config successfully", func() {
var keyPairName string

Expand Down
33 changes: 33 additions & 0 deletions test/utils/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ package utils
import (
"context"

"github.com/gardener/terraformer/pkg/terraformer"

. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

// TestObjects models a set of API objects used in tests
Expand Down Expand Up @@ -85,6 +88,36 @@ func PrepareTestObjects(ctx context.Context, c client.Client, namespacePrefix st
return o
}

// CleanupTestObjects take care to remove the finalizers of the secret and configmaps
func CleanupTestObjects(ctx context.Context, o *TestObjects) {
configurationConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: o.ConfigurationConfigMap.Name, Namespace: o.Namespace},
}
Expect(client.IgnoreNotFound(o.client.Get(ctx, ObjectKeyFromObject(configurationConfigMap), configurationConfigMap))).To(Succeed())
copyConfigurationConfigMap := configurationConfigMap.DeepCopy()
controllerutil.RemoveFinalizer(copyConfigurationConfigMap, terraformer.TerraformerFinalizer)
Expect(client.IgnoreNotFound(o.client.Patch(ctx, copyConfigurationConfigMap, client.MergeFrom(configurationConfigMap)))).To(Succeed())
Expect(client.IgnoreNotFound(o.client.Delete(ctx, copyConfigurationConfigMap))).To(Succeed())

stateConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: o.StateConfigMap.Name, Namespace: o.Namespace},
}
Expect(client.IgnoreNotFound(o.client.Get(ctx, ObjectKeyFromObject(stateConfigMap), stateConfigMap))).To(Succeed())
copyStateConfigMap := stateConfigMap.DeepCopy()
controllerutil.RemoveFinalizer(copyStateConfigMap, terraformer.TerraformerFinalizer)
Expect(client.IgnoreNotFound(o.client.Patch(ctx, copyStateConfigMap, client.MergeFrom(stateConfigMap)))).To(Succeed())
Expect(client.IgnoreNotFound(o.client.Delete(ctx, copyStateConfigMap))).To(Succeed())

variablesSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: o.VariablesSecret.Name, Namespace: o.Namespace},
}
Expect(client.IgnoreNotFound(o.client.Get(ctx, ObjectKeyFromObject(variablesSecret), variablesSecret))).To(Succeed())
copyVariablesSecret := variablesSecret.DeepCopy()
controllerutil.RemoveFinalizer(copyVariablesSecret, terraformer.TerraformerFinalizer)
Expect(client.IgnoreNotFound(o.client.Patch(ctx, copyVariablesSecret, client.MergeFrom(variablesSecret)))).To(Succeed())
Expect(client.IgnoreNotFound(o.client.Delete(ctx, copyVariablesSecret))).To(Succeed())
}

// Refresh retrieves a fresh copy of the objects from the API server, so that tests can make assertions on them.
func (o *TestObjects) Refresh() {
Expect(o.client.Get(o.ctx, ObjectKeyFromObject(o.ConfigurationConfigMap), o.ConfigurationConfigMap)).To(Succeed())
Expand Down

0 comments on commit baba72a

Please sign in to comment.