Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions internal/service/mandatorymodule/deletion/deletion_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package deletion

import (
"context"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
)

type UseCase interface {
ShouldExecute(ctx context.Context, mrm *v1beta2.ModuleReleaseMeta) (bool, error)
Execute(ctx context.Context, mrm *v1beta2.ModuleReleaseMeta) error
}

type Service struct {
orderedUseCases []UseCase
}

func NewService(skipNonMandatory UseCase,
ensureFinalizer UseCase,
skipNonDeleting UseCase,
deleteManifests UseCase,
removeFinalizer UseCase,
) *Service {
return &Service{
orderedUseCases: []UseCase{
skipNonMandatory, // if returns deletion.ErrMrmNotMandatory, controller should not requeue
ensureFinalizer,
skipNonDeleting, // if returns deletion.ErrMrmNotInDeletingState, controller should not requeue
deleteManifests,
removeFinalizer,
},
}
}

func (s *Service) HandleDeletion(ctx context.Context, mrm *v1beta2.ModuleReleaseMeta) error {
for _, useCase := range s.orderedUseCases {
shouldExecute, err := useCase.ShouldExecute(ctx, mrm)
if err != nil {
return err
}
if shouldExecute {
return useCase.Execute(ctx, mrm)
}
}
return nil
}
297 changes: 297 additions & 0 deletions internal/service/mandatorymodule/deletion/deletion_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
package deletion_test

import (
"context"
"errors"
"testing"

"github.com/stretchr/testify/require"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/internal/service/mandatorymodule/deletion"
)

func TestDeletionService_HandleDeletion_ExecutionOrder(t *testing.T) {
t.Parallel()

var executionOrder []string

skipNonMandatoryStub := &SkipNonMandatoryStub{StubName: "skipNonMandatory", ExecutionOrder: &executionOrder}
ensureFinalizerStub := &EnsureFinalizerStub{StubName: "ensureFinalizer", ExecutionOrder: &executionOrder}
skipNonDeletingStub := &SkipNonDeletingStub{StubName: "skipNonDeleting", ExecutionOrder: &executionOrder}
deleteManifestsStub := &DeleteManifestsStub{StubName: "deleteManifests", ExecutionOrder: &executionOrder}
removeFinalizerStub := &RemoveFinalizerStub{StubName: "removeFinalizer", ExecutionOrder: &executionOrder}

service := deletion.NewService(
skipNonMandatoryStub,
ensureFinalizerStub,
skipNonDeletingStub,
deleteManifestsStub,
removeFinalizerStub,
)
mrm := &v1beta2.ModuleReleaseMeta{}

for range 5 {
err := service.HandleDeletion(context.Background(), mrm)
require.NoError(t, err)
}

expectedOrder := []string{
"skipNonMandatory",
"ensureFinalizer",
"skipNonDeleting",
"deleteManifests",
"removeFinalizer",
}
require.Equal(t, expectedOrder, executionOrder)

require.True(t, skipNonMandatoryStub.ShouldExecuteCalled)
require.True(t, skipNonMandatoryStub.ExecuteCalled)
require.True(t, ensureFinalizerStub.ShouldExecuteCalled)
require.True(t, ensureFinalizerStub.ExecuteCalled)
require.True(t, skipNonDeletingStub.ShouldExecuteCalled)
require.True(t, skipNonDeletingStub.ExecuteCalled)
require.True(t, deleteManifestsStub.ShouldExecuteCalled)
require.True(t, deleteManifestsStub.ExecuteCalled)
require.True(t, removeFinalizerStub.ShouldExecuteCalled)
require.True(t, removeFinalizerStub.ExecuteCalled)
}

func TestDeletionService_HandleDeletion_ErrorPropagation(t *testing.T) {
t.Parallel()

var executionOrder []string

skipNonMandatoryErrorStub := &SkipNonMandatoryErrorStub{
StubName: "skipNonMandatory",
ExecutionOrder: &executionOrder,
}
ensureFinalizerStub := &EnsureFinalizerStub{StubName: "ensureFinalizer", ExecutionOrder: &executionOrder}
skipNonDeletingStub := &SkipNonDeletingStub{StubName: "skipNonDeleting", ExecutionOrder: &executionOrder}
deleteManifestsStub := &DeleteManifestsStub{StubName: "deleteManifests", ExecutionOrder: &executionOrder}
removeFinalizerStub := &RemoveFinalizerStub{StubName: "removeFinalizer", ExecutionOrder: &executionOrder}

service := deletion.NewService(
skipNonMandatoryErrorStub,
ensureFinalizerStub,
skipNonDeletingStub,
deleteManifestsStub,
removeFinalizerStub,
)
mrm := &v1beta2.ModuleReleaseMeta{}

for range 5 {
err := service.HandleDeletion(context.Background(), mrm)
require.Error(t, err)
require.Contains(t, err.Error(), "skipNonMandatory failed")
}

expectedOrder := []string{
"skipNonMandatory",
"skipNonMandatory",
"skipNonMandatory",
"skipNonMandatory",
"skipNonMandatory",
}
require.Equal(t, expectedOrder, executionOrder)
}

func TestDeletionService_HandleDeletion_ShouldExecuteError(t *testing.T) {
t.Parallel()

var executionOrder []string

skipNonMandatoryShouldExecuteErrorStub := &SkipNonMandatoryShouldExecuteErrorStub{
StubName: "skipNonMandatory",
ExecutionOrder: &executionOrder,
}
ensureFinalizerStub := &EnsureFinalizerStub{StubName: "ensureFinalizer", ExecutionOrder: &executionOrder}
skipNonDeletingStub := &SkipNonDeletingStub{StubName: "skipNonDeleting", ExecutionOrder: &executionOrder}
deleteManifestsStub := &DeleteManifestsStub{StubName: "deleteManifests", ExecutionOrder: &executionOrder}
removeFinalizerStub := &RemoveFinalizerStub{StubName: "removeFinalizer", ExecutionOrder: &executionOrder}

service := deletion.NewService(
skipNonMandatoryShouldExecuteErrorStub,
ensureFinalizerStub,
skipNonDeletingStub,
deleteManifestsStub,
removeFinalizerStub,
)
mrm := &v1beta2.ModuleReleaseMeta{}

err := service.HandleDeletion(context.Background(), mrm)
require.Error(t, err)
require.Contains(t, err.Error(), "shouldExecute failed")

require.Empty(t, executionOrder)

require.True(t, skipNonMandatoryShouldExecuteErrorStub.ShouldExecuteCalled)
require.False(t, skipNonMandatoryShouldExecuteErrorStub.ExecuteCalled)
require.False(t, ensureFinalizerStub.ShouldExecuteCalled)
require.False(t, ensureFinalizerStub.ExecuteCalled)
require.False(t, skipNonDeletingStub.ShouldExecuteCalled)
require.False(t, skipNonDeletingStub.ExecuteCalled)
require.False(t, deleteManifestsStub.ShouldExecuteCalled)
require.False(t, deleteManifestsStub.ExecuteCalled)
require.False(t, removeFinalizerStub.ShouldExecuteCalled)
require.False(t, removeFinalizerStub.ExecuteCalled)
}

// Stubs for the use cases to track execution order and calls

type SkipNonMandatoryStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *SkipNonMandatoryStub) ShouldExecute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) (bool, error) {
if stub.ShouldExecuteCalled {
return false, nil
}
stub.ShouldExecuteCalled = true
return true, nil
}

func (stub *SkipNonMandatoryStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return nil
}

type EnsureFinalizerStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *EnsureFinalizerStub) ShouldExecute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) (bool, error) {
if stub.ShouldExecuteCalled {
return false, nil
}
stub.ShouldExecuteCalled = true
return true, nil
}

func (stub *EnsureFinalizerStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return nil
}

type SkipNonDeletingStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *SkipNonDeletingStub) ShouldExecute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) (bool, error) {
if stub.ShouldExecuteCalled {
return false, nil
}
stub.ShouldExecuteCalled = true
return true, nil
}

func (stub *SkipNonDeletingStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return nil
}

type DeleteManifestsStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *DeleteManifestsStub) ShouldExecute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) (bool, error) {
if stub.ShouldExecuteCalled {
return false, nil
}
stub.ShouldExecuteCalled = true
return true, nil
}

func (stub *DeleteManifestsStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return nil
}

type RemoveFinalizerStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *RemoveFinalizerStub) ShouldExecute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) (bool, error) {
if stub.ShouldExecuteCalled {
return false, nil
}
stub.ShouldExecuteCalled = true
return true, nil
}

func (stub *RemoveFinalizerStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return nil
}

type SkipNonMandatoryErrorStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *SkipNonMandatoryErrorStub) ShouldExecute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) (bool, error) {
stub.ShouldExecuteCalled = true
return true, nil
}

func (stub *SkipNonMandatoryErrorStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return errors.New("skipNonMandatory failed")
}

type SkipNonMandatoryShouldExecuteErrorStub struct {
ShouldExecuteCalled bool
ExecuteCalled bool
ExecutionOrder *[]string
StubName string
}

func (stub *SkipNonMandatoryShouldExecuteErrorStub) ShouldExecute(_ context.Context,
_ *v1beta2.ModuleReleaseMeta,
) (bool, error) {
stub.ShouldExecuteCalled = true
return false, errors.New("shouldExecute failed")
}

func (stub *SkipNonMandatoryShouldExecuteErrorStub) Execute(_ context.Context, _ *v1beta2.ModuleReleaseMeta) error {
stub.ExecuteCalled = true
if stub.ExecutionOrder != nil {
*stub.ExecutionOrder = append(*stub.ExecutionOrder, stub.StubName)
}
return nil
}
8 changes: 8 additions & 0 deletions internal/service/mandatorymodule/deletion/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package deletion

import "errors"

var (
ErrMrmNotMandatory = errors.New("ModuleReleaseMeta is not a mandatory module")
ErrMrmNotInDeletingState = errors.New("ModuleReleaseMeta not in deleting state")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package usecases

import (
"context"
"fmt"

apimetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
)

type ManifestRepo interface {
ListAllForModule(ctx context.Context, moduleName string) ([]apimetav1.PartialObjectMetadata, error)
DeleteAllForModule(ctx context.Context, moduleName string) error
}

// DeleteManifests is responsible for deleting all manifests associated with a ModuleReleaseMeta.
type DeleteManifests struct {
repo ManifestRepo
}

func NewDeleteManifests(repo ManifestRepo) *DeleteManifests {
return &DeleteManifests{repo: repo}
}

func (d *DeleteManifests) ShouldExecute(ctx context.Context, mrm *v1beta2.ModuleReleaseMeta) (bool, error) {
manifests, err := d.repo.ListAllForModule(ctx, mrm.Name)
if err != nil {
return false, fmt.Errorf("failed to list manifests for module %s: %w", mrm.Name, err)
}
return len(manifests) > 0, nil
}

func (d *DeleteManifests) Execute(ctx context.Context, mrm *v1beta2.ModuleReleaseMeta) error {
if err := d.repo.DeleteAllForModule(ctx, mrm.Name); err != nil {
return fmt.Errorf("failed to delete manifests for module %s: %w", mrm.Name, err)
}
return nil
}
Loading
Loading