Skip to content

Commit

Permalink
Add delete state
Browse files Browse the repository at this point in the history
  • Loading branch information
m00g3n committed Jul 2, 2024
1 parent 5053ed1 commit 25c4cad
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 17 deletions.
18 changes: 11 additions & 7 deletions api/v1/runtime_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import (
//+kubebuilder:printcolumn:name="SHOOT-NAME",type=string,JSONPath=`.metadata.labels.kyma-project\.io/shoot-name`
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

const Finalizer = "runtime-controller.infrastructure-manager.kyma-project.io/deletion-hook"
const (
Finalizer = "runtime-controller.infrastructure-manager.kyma-project.io/deletion-hook"
AnnotationGardenerCloudDelConfirmation = "confirmation.gardener.cloud/deletion"
)

const (
RuntimeStateReady = "Ready"
Expand Down Expand Up @@ -60,12 +63,13 @@ const (
ConditionReasonConfigurationCompleted = RuntimeConditionReason("ConfigurationCompleted")
ConditionReasonConfigurationErr = RuntimeConditionReason("ConfigurationError")

ConditionReasonDeletion = RuntimeConditionReason("Deletion")
ConditionReasonDeletionErr = RuntimeConditionReason("DeletionErr")
ConditionReasonConversionError = RuntimeConditionReason("ConversionErr")
ConditionReasonCreationError = RuntimeConditionReason("CreationErr")
ConditionReasonGardenerError = RuntimeConditionReason("GardenerErr")
ConditionReasonDeleted = RuntimeConditionReason("Deleted")
ConditionReasonDeletion = RuntimeConditionReason("Deletion")
ConditionReasonDeletionErr = RuntimeConditionReason("DeletionErr")
ConditionReasonConversionError = RuntimeConditionReason("ConversionErr")
ConditionReasonCreationError = RuntimeConditionReason("CreationErr")
ConditionReasonGardenerError = RuntimeConditionReason("GardenerErr")
ConditionReasonSerializationError = RuntimeConditionReason("SerializationErr")
ConditionReasonDeleted = RuntimeConditionReason("Deleted")
)

//+kubebuilder:object:root=true
Expand Down
58 changes: 54 additions & 4 deletions internal/controller/runtime/fsm/runtime_fsm_delete_shoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,47 @@ package fsm

import (
"context"
"encoding/json"

imv1 "github.com/kyma-project/infrastructure-manager/api/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
)

func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl.Result, error) {
if !isGardenerCloudDelConfirmationSet(s.shoot.Annotations) {
m.log.Info("patching shoot with del-confirmation")
// workaround for Gardener client
s.shoot.Kind = "Shoot"
s.shoot.APIVersion = "core.gardener.cloud/v1beta1"
s.shoot.Annotations = addGardenerCloudDelConfirmation(s.shoot.Annotations)
s.shoot.ManagedFields = nil
// attempt to marshall patched instance
shootData, err := json.Marshal(&s.shoot)
if err != nil {
// unrecoverable error
s.instance.UpdateStateDeletion(
imv1.ConditionTypeRuntimeProvisioned,
imv1.ConditionReasonSerializationError,
"False",
err.Error())
return updateStatusAndStop()
}
// see: https://gardener.cloud/docs/gardener/projects/#four-eyes-principle-for-resource-deletion
if s.shoot, err = m.ShootClient.Patch(ctx, s.shoot.Name, types.ApplyPatchType, shootData,
metav1.PatchOptions{
FieldManager: "kim",
Force: ptr.To(true),
}); err != nil {
m.log.Error(err, "unable to patch shoot:")
return requeue()
}
}

if !s.instance.IsStateWithConditionSet(imv1.RuntimeStateTerminating, imv1.ConditionTypeRuntimeProvisioned, imv1.ConditionReasonDeletion) {
m.log.Info("setting state to in deletion")
s.instance.UpdateStateDeletion(
imv1.ConditionTypeRuntimeProvisioned,
imv1.ConditionReasonDeletion,
Expand All @@ -19,8 +52,8 @@ func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl
return updateStatusAndRequeue()
}

err := m.ShootClient.Delete(ctx, s.instance.Name, v1.DeleteOptions{})

m.log.Info("deleting shoot")
err := m.ShootClient.Delete(ctx, s.instance.Name, metav1.DeleteOptions{})
if err != nil {
m.log.Error(err, "Failed to delete gardener Shoot")

Expand All @@ -32,5 +65,22 @@ func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl
)
return updateStatusAndRequeue()
}
return updateStatusAndStop()

return updateStatusAndRequeue()
}

func isGardenerCloudDelConfirmationSet(a map[string]string) bool {
if len(a) == 0 {
return false
}
val, found := a[imv1.AnnotationGardenerCloudDelConfirmation]
return found && (val == "true")
}

func addGardenerCloudDelConfirmation(a map[string]string) map[string]string {
if len(a) == 0 {
a = map[string]string{}
}
a[imv1.AnnotationGardenerCloudDelConfirmation] = "true"
return a
}
64 changes: 64 additions & 0 deletions internal/controller/runtime/fsm/runtime_fsm_delete_shoot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package fsm

import (
"fmt"
"testing"

v1 "github.com/kyma-project/infrastructure-manager/api/v1"
)

func Test_addGardenerCloudDelConfirmation(t *testing.T) {
instance := v1.Runtime{}
actual := addGardenerCloudDelConfirmation(instance.Annotations)
if _, found := actual[v1.AnnotationGardenerCloudDelConfirmation]; !found {
t.Errorf("actual map should contain '%s' annotation", v1.AnnotationGardenerCloudDelConfirmation)
}
}

func Test_IsGardenerCloudDelConfirmation(t *testing.T) {
var cases = []struct {
annotations map[string]string
expected bool
}{
{
expected: false,
},
{
annotations: (map[string]string{}),
expected: false,
},
{
annotations: map[string]string{"test": "me"},
expected: false,
},
{
annotations: map[string]string{
v1.AnnotationGardenerCloudDelConfirmation: "true",
"test": "me",
},
expected: true,
},
{
annotations: map[string]string{
v1.AnnotationGardenerCloudDelConfirmation: "anything",
"test": "me",
},
expected: false,
},
{
annotations: map[string]string{
v1.AnnotationGardenerCloudDelConfirmation: "",
},
expected: false,
},
}

for i, tt := range cases {
t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) {
if actual := isGardenerCloudDelConfirmationSet(tt.annotations); actual != tt.expected {
t.Errorf("expected IsGardenerCloudDelConfirmation == %t",
tt.expected)
}
})
}
}
9 changes: 6 additions & 3 deletions internal/controller/runtime/fsm/runtime_fsm_initialise.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,18 @@ func sFnInitialize(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl.
return switchState(sFnPrepareCluster) // wait for pending shoot operation to complete
}

if !instanceIsNotBeingDeleted && instanceHasFinalizer && s.shoot != nil {
m.log.Info("Instance is being deleted")
shootInOrAfterDeleting := s.shoot == nil || !s.shoot.GetDeletionTimestamp().IsZero()

if !instanceIsNotBeingDeleted && instanceHasFinalizer && !shootInOrAfterDeleting {
m.log.Info("Delete instance resources")
return switchState(sFnDeleteShoot)
}

if !instanceIsNotBeingDeleted && instanceHasFinalizer && s.shoot == nil {
if !instanceIsNotBeingDeleted && instanceHasFinalizer && shootInOrAfterDeleting {
return removeFinalizerAndStop(ctx, m, s)
}

m.log.Info("noting to reconcile, stopping sfm")
return stop()
}

Expand Down
4 changes: 2 additions & 2 deletions internal/controller/runtime/runtime_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (r *RuntimeReconciler) Reconcile(ctx context.Context, request ctrl.Request)

if err := r.Get(ctx, request.NamespacedName, &runtime); err != nil {
return ctrl.Result{
Requeue: true,
Requeue: false,
}, client.IgnoreNotFound(err)
}

Expand Down Expand Up @@ -90,6 +90,6 @@ func NewRuntimeReconciler(mgr ctrl.Manager, shootClient gardener.ShootClient, lo
func (r *RuntimeReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&imv1.Runtime{}).
WithEventFilter(predicate.And(predicate.GenerationChangedPredicate{})).
WithEventFilter(predicate.GenerationChangedPredicate{}).
Complete(r)
}
2 changes: 2 additions & 0 deletions internal/gardener/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

gardener_api "github.com/gardener/gardener/pkg/apis/core/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
Expand All @@ -16,6 +17,7 @@ type ShootClient interface {
Create(ctx context.Context, shoot *gardener_api.Shoot, opts v1.CreateOptions) (*gardener_api.Shoot, error)
Get(ctx context.Context, name string, opts v1.GetOptions) (*gardener_api.Shoot, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *gardener_api.Shoot, err error)
// List(ctx context.Context, opts v1.ListOptions) (*gardener.ShootList, error)
}

Expand Down
53 changes: 52 additions & 1 deletion internal/gardener/mocks/ShootClient.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 25c4cad

Please sign in to comment.