Skip to content

Commit c46562c

Browse files
authored
Updated community module removal flow (#2804)
1 parent b10d8b1 commit c46562c

File tree

5 files changed

+177
-34
lines changed

5 files changed

+177
-34
lines changed

internal/modules/fake/moduletemplates.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type ModuleTemplatesRepo struct {
1616
ReturnCommunityByName []kyma.ModuleTemplate
1717
ReturnCommunityInstalledByName []kyma.ModuleTemplate
1818
ReturnRunningAssociatedResourcesOfModule []unstructured.Unstructured
19+
ReturnUserDefinedResourcesOfModule []unstructured.Unstructured
1920
ReturnResources []map[string]any
2021
ReturnInstalledManager *unstructured.Unstructured
2122
ReturnDeleteResourceReturnWatcher watch.Interface
@@ -60,11 +61,15 @@ func (r *ModuleTemplatesRepo) RunningAssociatedResourcesOfModule(_ context.Conte
6061
return r.ReturnRunningAssociatedResourcesOfModule, r.RunningAssociatedResourcesOfModuleErr
6162
}
6263

64+
func (r *ModuleTemplatesRepo) RunningUserDefinedResourcesOfModule(_ context.Context, _ kyma.ModuleTemplate) ([]unstructured.Unstructured, error) {
65+
return r.ReturnUserDefinedResourcesOfModule, r.RunningAssociatedResourcesOfModuleErr
66+
}
67+
6368
func (r *ModuleTemplatesRepo) Resources(_ context.Context, _ kyma.ModuleTemplate) ([]map[string]any, error) {
6469
return r.ReturnResources, r.ResourcesErr
6570
}
6671

67-
func (r *ModuleTemplatesRepo) DeleteResourceReturnWatcher(_ context.Context, _ map[string]any) (watch.Interface, error) {
72+
func (r *ModuleTemplatesRepo) DeleteResourceReturnWatcher(_ context.Context, _ unstructured.Unstructured) (watch.Interface, error) {
6873
return r.ReturnDeleteResourceReturnWatcher, r.DeleteResourceReturnWatcherErr
6974
}
7075

internal/modules/repo/moduletemplates.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ type ModuleTemplatesRepository interface {
2424
CommunityByName(ctx context.Context, moduleName string) ([]kyma.ModuleTemplate, error)
2525
CommunityInstalledByName(ctx context.Context, moduleName string) ([]kyma.ModuleTemplate, error)
2626
RunningAssociatedResourcesOfModule(ctx context.Context, moduleTemplate kyma.ModuleTemplate) ([]unstructured.Unstructured, error)
27+
RunningUserDefinedResourcesOfModule(ctx context.Context, moduleTemplate kyma.ModuleTemplate) ([]unstructured.Unstructured, error)
2728
Resources(ctx context.Context, moduleTemplate kyma.ModuleTemplate) ([]map[string]any, error)
28-
DeleteResourceReturnWatcher(ctx context.Context, resource map[string]any) (watch.Interface, error)
29+
DeleteResourceReturnWatcher(ctx context.Context, resource unstructured.Unstructured) (watch.Interface, error)
2930
InstalledManager(ctx context.Context, moduleTemplate kyma.ModuleTemplate) (*unstructured.Unstructured, error)
3031
ExternalCommunity(ctx context.Context) ([]kyma.ModuleTemplate, error)
3132
ExternalCommunityByNameAndVersion(ctx context.Context, moduleName, version string) ([]kyma.ModuleTemplate, error)
@@ -121,18 +122,33 @@ func (r *moduleTemplatesRepo) CommunityInstalledByName(ctx context.Context, modu
121122
return installedModules, nil
122123
}
123124

124-
func (r *moduleTemplatesRepo) RunningAssociatedResourcesOfModule(ctx context.Context, moduleTemplate kyma.ModuleTemplate) ([]unstructured.Unstructured, error) {
125-
associatedResources := moduleTemplate.Spec.AssociatedResources
125+
func (r *moduleTemplatesRepo) RunningUserDefinedResourcesOfModule(ctx context.Context, moduleTemplate kyma.ModuleTemplate) ([]unstructured.Unstructured, error) {
126+
associatedResources, err := r.RunningAssociatedResourcesOfModule(ctx, moduleTemplate)
127+
if err != nil {
128+
return nil, fmt.Errorf("failed to get module %5s running resources: %v", moduleTemplate.Name, err)
129+
}
130+
126131
operator := moduleTemplate.Spec.Data
127132

128-
var runningResources []unstructured.Unstructured
133+
var userDefinedResources []unstructured.Unstructured
129134

130135
for _, associatedResource := range associatedResources {
131-
associatedResourceAPIVersion := associatedResource.Group + "/" + associatedResource.Version
132-
if associatedResource.Kind == operator.GetKind() && associatedResourceAPIVersion == operator.GetAPIVersion() {
136+
if associatedResource.GetKind() == operator.GetKind() && associatedResource.GetAPIVersion() == operator.GetAPIVersion() {
133137
continue
134138
}
135139

140+
userDefinedResources = append(userDefinedResources, associatedResource)
141+
}
142+
143+
return userDefinedResources, nil
144+
}
145+
146+
func (r *moduleTemplatesRepo) RunningAssociatedResourcesOfModule(ctx context.Context, moduleTemplate kyma.ModuleTemplate) ([]unstructured.Unstructured, error) {
147+
associatedResources := moduleTemplate.Spec.AssociatedResources
148+
149+
var runningResources []unstructured.Unstructured
150+
151+
for _, associatedResource := range associatedResources {
136152
list, err := r.client.RootlessDynamic().List(ctx, &unstructured.Unstructured{
137153
Object: map[string]any{
138154
"apiVersion": associatedResource.Group + "/" + associatedResource.Version,
@@ -186,16 +202,15 @@ func (r *moduleTemplatesRepo) Resources(ctx context.Context, moduleTemplate kyma
186202
return parsedResources, nil
187203
}
188204

189-
func (r *moduleTemplatesRepo) DeleteResourceReturnWatcher(ctx context.Context, resource map[string]any) (watch.Interface, error) {
190-
u := &unstructured.Unstructured{Object: resource}
191-
watcher, err := r.client.RootlessDynamic().WatchSingleResource(ctx, u)
205+
func (r *moduleTemplatesRepo) DeleteResourceReturnWatcher(ctx context.Context, resource unstructured.Unstructured) (watch.Interface, error) {
206+
watcher, err := r.client.RootlessDynamic().WatchSingleResource(ctx, &resource)
192207
if err != nil {
193-
return nil, fmt.Errorf("failed to watch resource %s: %v", u.GetName(), err)
208+
return nil, fmt.Errorf("failed to watch resource %s: %v", resource.GetName(), err)
194209
}
195210

196-
err = r.client.RootlessDynamic().Remove(ctx, u, false)
211+
err = r.client.RootlessDynamic().Remove(ctx, &resource, false)
197212
if err != nil {
198-
return nil, fmt.Errorf("failed to remove resource %s: %v", u.GetName(), err)
213+
return nil, fmt.Errorf("failed to remove resource %s: %v", resource.GetName(), err)
199214
}
200215

201216
return watcher, nil

internal/modules/repo/moduletemplates_test.go

Lines changed: 124 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ func TestModuleTemplatesRepo_RunningAssociatedResourcesOfModule(t *testing.T) {
404404
require.Len(t, result, 0)
405405
})
406406

407-
t.Run("excludes spec.data resource", func(t *testing.T) {
407+
t.Run("includes spec.data resource", func(t *testing.T) {
408408
fakeRootlessDynamicClient := fake.RootlessDynamicClient{
409409
ReturnListObjs: &unstructured.UnstructuredList{
410410
Items: []unstructured.Unstructured{
@@ -442,8 +442,9 @@ func TestModuleTemplatesRepo_RunningAssociatedResourcesOfModule(t *testing.T) {
442442
resources, err := repo.RunningAssociatedResourcesOfModule(context.Background(), mod)
443443

444444
require.NoError(t, err)
445-
require.Len(t, resources, 1)
445+
require.Len(t, resources, 2)
446446
require.Equal(t, "res1", resources[0].GetName())
447+
require.Equal(t, "res1", resources[1].GetName())
447448
})
448449

449450
t.Run("running resources found", func(t *testing.T) {
@@ -480,6 +481,110 @@ func TestModuleTemplatesRepo_RunningAssociatedResourcesOfModule(t *testing.T) {
480481
})
481482
}
482483

484+
func TestModuleTemplatesRepo_RunningUserDefinedResourcesOfModule(t *testing.T) {
485+
t.Run("fails to list running resources", func(t *testing.T) {
486+
fakeRootlessDynamicClient := fake.RootlessDynamicClient{
487+
ReturnErr: errors.New("list-error"),
488+
}
489+
fakeKubeClient := fake.KubeClient{
490+
TestRootlessDynamicInterface: &fakeRootlessDynamicClient,
491+
}
492+
repo := NewModuleTemplatesRepo(&fakeKubeClient)
493+
mod := kyma.ModuleTemplate{
494+
Spec: kyma.ModuleTemplateSpec{
495+
AssociatedResources: []metav1.GroupVersionKind{{Group: "g", Version: "v1", Kind: "Kind"}},
496+
},
497+
}
498+
499+
result, err := repo.RunningUserDefinedResourcesOfModule(context.Background(), mod)
500+
501+
require.NoError(t, err)
502+
require.Len(t, result, 0)
503+
})
504+
505+
t.Run("excludes spec.data resource", func(t *testing.T) {
506+
fakeRootlessDynamicClient := fake.RootlessDynamicClient{
507+
ReturnListObjs: &unstructured.UnstructuredList{
508+
Items: []unstructured.Unstructured{
509+
{
510+
Object: map[string]any{
511+
"apiVersion": "g/v1",
512+
"kind": "Operator",
513+
"metadata": map[string]any{
514+
"name": "res1",
515+
},
516+
},
517+
},
518+
},
519+
},
520+
}
521+
522+
fakeKubeClient := fake.KubeClient{
523+
TestRootlessDynamicInterface: &fakeRootlessDynamicClient,
524+
}
525+
repo := NewModuleTemplatesRepo(&fakeKubeClient)
526+
527+
mod := kyma.ModuleTemplate{
528+
Spec: kyma.ModuleTemplateSpec{
529+
AssociatedResources: []metav1.GroupVersionKind{
530+
{Group: "g", Version: "v1", Kind: "Kind"},
531+
{Group: "g", Version: "v1", Kind: "Operator"},
532+
},
533+
Data: unstructured.Unstructured{
534+
Object: map[string]any{
535+
"apiVersion": "g/v1",
536+
"kind": "Operator",
537+
},
538+
},
539+
},
540+
}
541+
542+
resources, err := repo.RunningUserDefinedResourcesOfModule(context.Background(), mod)
543+
544+
require.NoError(t, err)
545+
require.Len(t, resources, 0)
546+
})
547+
548+
t.Run("running resources found", func(t *testing.T) {
549+
fakeRootlessDynamicClient := fake.RootlessDynamicClient{
550+
ReturnListObjs: &unstructured.UnstructuredList{
551+
Items: []unstructured.Unstructured{
552+
{
553+
Object: map[string]any{
554+
"metadata": map[string]any{
555+
"name": "res1",
556+
},
557+
},
558+
},
559+
},
560+
},
561+
}
562+
563+
fakeKubeClient := fake.KubeClient{
564+
TestRootlessDynamicInterface: &fakeRootlessDynamicClient,
565+
}
566+
repo := NewModuleTemplatesRepo(&fakeKubeClient)
567+
568+
mod := kyma.ModuleTemplate{
569+
Spec: kyma.ModuleTemplateSpec{
570+
AssociatedResources: []metav1.GroupVersionKind{{Group: "g", Version: "v1", Kind: "Kind"}},
571+
Data: unstructured.Unstructured{
572+
Object: map[string]any{
573+
"apiVersion": "g/v1",
574+
"kind": "Operator",
575+
},
576+
},
577+
},
578+
}
579+
580+
resources, err := repo.RunningUserDefinedResourcesOfModule(context.Background(), mod)
581+
582+
require.NoError(t, err)
583+
require.Len(t, resources, 1)
584+
require.Equal(t, "res1", resources[0].GetName())
585+
})
586+
}
587+
483588
func TestModuleTemplatesRepo_DeleteResourceReturnWatcher(t *testing.T) {
484589
t.Run("fails to watch resource", func(t *testing.T) {
485590
fakeRootlessDynamicClient := fake.RootlessDynamicClient{
@@ -488,9 +593,11 @@ func TestModuleTemplatesRepo_DeleteResourceReturnWatcher(t *testing.T) {
488593
fakeKubeClient := fake.KubeClient{
489594
TestRootlessDynamicInterface: &fakeRootlessDynamicClient,
490595
}
491-
resource := map[string]any{
492-
"metadata": map[string]any{
493-
"name": "test-resource",
596+
resource := unstructured.Unstructured{
597+
Object: map[string]any{
598+
"metadata": map[string]any{
599+
"name": "test-resource",
600+
},
494601
},
495602
}
496603
repo := NewModuleTemplatesRepo(&fakeKubeClient)
@@ -511,9 +618,11 @@ func TestModuleTemplatesRepo_DeleteResourceReturnWatcher(t *testing.T) {
511618
fakeKubeClient := fake.KubeClient{
512619
TestRootlessDynamicInterface: &fakeRootlessDynamicClient,
513620
}
514-
resource := map[string]any{
515-
"metadata": map[string]any{
516-
"name": "test-resoutce",
621+
resource := unstructured.Unstructured{
622+
Object: map[string]any{
623+
"metadata": map[string]any{
624+
"name": "test-resoutce",
625+
},
517626
},
518627
}
519628
repo := NewModuleTemplatesRepo(&fakeKubeClient)
@@ -533,19 +642,22 @@ func TestModuleTemplatesRepo_DeleteResourceReturnWatcher(t *testing.T) {
533642
fakeKubeClient := fake.KubeClient{
534643
TestRootlessDynamicInterface: &fakeRootlessDynamicClient,
535644
}
536-
resource := map[string]any{
537-
"metadata": map[string]any{
538-
"name": "test-resource",
645+
resource := unstructured.Unstructured{
646+
Object: map[string]any{
647+
"metadata": map[string]any{
648+
"name": "test-resource",
649+
},
539650
},
540651
}
652+
541653
repo := NewModuleTemplatesRepo(&fakeKubeClient)
542654

543655
result, err := repo.DeleteResourceReturnWatcher(context.Background(), resource)
544656

545657
require.Nil(t, err)
546658
require.NotNil(t, result)
547659
require.Equal(t, result, fakeWatcher)
548-
require.Contains(t, fakeRootlessDynamicClient.RemovedObjs, unstructured.Unstructured{Object: resource})
660+
require.Contains(t, fakeRootlessDynamicClient.RemovedObjs, resource)
549661
})
550662
}
551663

internal/modules/uninstall.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,36 @@ func uninstall(printer *out.Printer, ctx context.Context, repo repo.ModuleTempla
2323
moduleName := moduleTemplate.Spec.ModuleName
2424
printer.Msgfln("removing %s community module from the target Kyma environment", moduleName)
2525

26+
associatedResources, err := repo.RunningAssociatedResourcesOfModule(ctx, *moduleTemplate)
27+
if err != nil {
28+
return clierror.Wrap(err, clierror.New(fmt.Sprintf("failed to get resources for the module %v: %v", moduleName, err)))
29+
}
30+
2631
moduleResources, err := repo.Resources(ctx, *moduleTemplate)
2732
if err != nil {
28-
return clierror.Wrap(err, clierror.New(fmt.Sprintf("failed to get resources for the module %v", moduleName)))
33+
return clierror.Wrap(err, clierror.New(fmt.Sprintf("failed to get resources for the module %v: %v", moduleName, err)))
2934
}
3035

3136
// We want to remove resources in the reversed order
3237
slices.Reverse(moduleResources)
38+
slices.Reverse(associatedResources)
3339

34-
removedSuccessfully := true
40+
moduleResourcesUnstruct := []unstructured.Unstructured{}
41+
for _, mr := range moduleResources {
42+
moduleResourcesUnstruct = append(moduleResourcesUnstruct, unstructured.Unstructured{Object: mr})
43+
}
44+
45+
resourcesToDelete := slices.Concat(moduleResourcesUnstruct, associatedResources)
3546

36-
for _, resource := range moduleResources {
47+
removedSuccessfully := true
48+
for _, resource := range resourcesToDelete {
3749
resourceWatcher, err := repo.DeleteResourceReturnWatcher(ctx, resource)
38-
r := unstructured.Unstructured{Object: resource}
3950
if err != nil {
4051
removedSuccessfully = false
41-
printer.Msgfln("failed to delete resource %s (%s): %v", r.GetName(), r.GetKind(), err)
52+
printer.Msgfln("failed to delete resource %s (%s): %v", resource.GetName(), resource.GetKind(), err)
4253
continue
4354
}
44-
printer.Msgfln("waiting for resource deletion: %s (%s)", r.GetName(), r.GetKind())
55+
printer.Msgfln("waiting for resource deletion: %s (%s)", resource.GetName(), resource.GetKind())
4556
timeoutCtx, cancel := context.WithTimeout(ctx, time.Second*100)
4657
defer cancel()
4758

@@ -61,7 +72,7 @@ func uninstall(printer *out.Printer, ctx context.Context, repo repo.ModuleTempla
6172
}
6273

6374
func GetRunningResourcesOfCommunityModule(ctx context.Context, repo repo.ModuleTemplatesRepository, moduleTemplate kyma.ModuleTemplate) ([]string, clierror.Error) {
64-
runningResources, err := repo.RunningAssociatedResourcesOfModule(ctx, moduleTemplate)
75+
runningResources, err := repo.RunningUserDefinedResourcesOfModule(ctx, moduleTemplate)
6576
if err != nil {
6677
return nil, clierror.Wrap(err, clierror.New(fmt.Sprintf("failed to retrieve running resources of the %s module", moduleTemplate.Spec.ModuleName)))
6778
}

internal/modules/uninstall_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func TestGetRunningResourcesOfCommunityModule(t *testing.T) {
5252
ctx := context.Background()
5353
fakeModuleTemplatesRepo := modulesfake.ModuleTemplatesRepo{
5454
ReturnCommunityInstalledByName: []kyma.ModuleTemplate{{}},
55-
ReturnRunningAssociatedResourcesOfModule: []unstructured.Unstructured{
55+
ReturnUserDefinedResourcesOfModule: []unstructured.Unstructured{
5656
{
5757
Object: map[string]any{
5858
"metadata": map[string]any{
@@ -99,7 +99,7 @@ func TestDisableCommunity(t *testing.T) {
9999

100100
expectedCliErr := clierror.Wrap(
101101
errors.New("ResourcesError"),
102-
clierror.New("failed to get resources for the module test"),
102+
clierror.New("failed to get resources for the module test: ResourcesError"),
103103
)
104104

105105
require.NotNil(t, err)

0 commit comments

Comments
 (0)