1 package delete
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "reflect"
8 "testing"
9
10 "github.com/devfile/library/v2/pkg/devfile/parser"
11 "github.com/devfile/library/v2/pkg/testingutil/filesystem"
12 "github.com/golang/mock/gomock"
13 "github.com/google/go-cmp/cmp"
14
15 appsv1 "k8s.io/api/apps/v1"
16 corev1 "k8s.io/api/core/v1"
17 kerrors "k8s.io/apimachinery/pkg/api/errors"
18 "k8s.io/apimachinery/pkg/api/meta"
19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
21 "k8s.io/apimachinery/pkg/runtime/schema"
22
23 "github.com/redhat-developer/odo/pkg/exec"
24 "github.com/redhat-developer/odo/pkg/kclient"
25 odolabels "github.com/redhat-developer/odo/pkg/labels"
26 odocontext "github.com/redhat-developer/odo/pkg/odo/context"
27 "github.com/redhat-developer/odo/pkg/platform"
28 "github.com/redhat-developer/odo/pkg/podman"
29 odoTestingUtil "github.com/redhat-developer/odo/pkg/testingutil"
30 "github.com/redhat-developer/odo/pkg/util"
31 )
32
33 const (
34 appName = "app"
35 )
36
37 func TestDeleteComponentClient_ListClusterResourcesToDelete(t *testing.T) {
38 res1 := getUnstructured("dep1", "deployment", "v1", "")
39 res2 := getUnstructured("svc1", "service", "v1", "")
40
41 selectorForMode := func(mode string) string {
42 selector := "app.kubernetes.io/instance=my-component,app.kubernetes.io/managed-by=odo,app.kubernetes.io/part-of=app"
43 if mode != "" {
44 selector += fmt.Sprintf(",odo.dev/mode=%s", mode)
45 }
46 return selector
47 }
48
49 type fields struct {
50 kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface
51 }
52 type args struct {
53 componentName string
54 namespace string
55 mode string
56 }
57 tests := []struct {
58 name string
59 fields fields
60 args args
61 want []unstructured.Unstructured
62 wantErr bool
63 }{
64 {
65 name: "no resource found",
66 fields: fields{
67 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
68 client := kclient.NewMockClientInterface(ctrl)
69 client.EXPECT().GetAllResourcesFromSelector(selectorForMode(odolabels.ComponentAnyMode), "my-ns").Return(nil, nil)
70 return client
71 },
72 },
73 args: args{
74 componentName: "my-component",
75 namespace: "my-ns",
76 },
77 wantErr: false,
78 want: nil,
79 },
80 {
81 name: "2 unrelated resources found",
82 fields: fields{
83 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
84 var resources []unstructured.Unstructured
85 resources = append(resources, res1, res2)
86 client := kclient.NewMockClientInterface(ctrl)
87 client.EXPECT().GetAllResourcesFromSelector(selectorForMode(odolabels.ComponentAnyMode), "my-ns").Return(resources, nil)
88 return client
89 },
90 },
91 args: args{
92 componentName: "my-component",
93 namespace: "my-ns",
94 },
95 wantErr: false,
96 want: []unstructured.Unstructured{res1, res2},
97 },
98 {
99 name: "2 resources found, one owned by the other",
100 fields: fields{
101 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
102 var resources []unstructured.Unstructured
103 res1.SetOwnerReferences([]metav1.OwnerReference{
104 {
105 APIVersion: res2.GetAPIVersion(),
106 Kind: res2.GetKind(),
107 Name: res2.GetName(),
108 },
109 })
110 resources = append(resources, res1, res2)
111 client := kclient.NewMockClientInterface(ctrl)
112 client.EXPECT().GetAllResourcesFromSelector(selectorForMode(odolabels.ComponentAnyMode), "my-ns").Return(resources, nil)
113 return client
114 },
115 },
116 args: args{
117 componentName: "my-component",
118 namespace: "my-ns",
119 },
120 wantErr: false,
121 want: []unstructured.Unstructured{res2},
122 },
123 {
124 name: "returning Dev resources only",
125 fields: fields{
126 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
127 client := kclient.NewMockClientInterface(ctrl)
128 client.EXPECT().GetAllResourcesFromSelector(selectorForMode(odolabels.ComponentDevMode), "my-ns").Return([]unstructured.Unstructured{res1}, nil)
129 return client
130 },
131 },
132 args: args{
133 componentName: "my-component",
134 namespace: "my-ns",
135 mode: odolabels.ComponentDevMode,
136 },
137 wantErr: false,
138 want: []unstructured.Unstructured{res1},
139 },
140 {
141 name: "returning Deploy resources only",
142 fields: fields{
143 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
144 client := kclient.NewMockClientInterface(ctrl)
145 client.EXPECT().GetAllResourcesFromSelector(selectorForMode(odolabels.ComponentDeployMode), "my-ns").Return([]unstructured.Unstructured{res2}, nil)
146 return client
147 },
148 },
149 args: args{
150 componentName: "my-component",
151 namespace: "my-ns",
152 mode: odolabels.ComponentDeployMode,
153 },
154 wantErr: false,
155 want: []unstructured.Unstructured{res2},
156 },
157 }
158 for _, tt := range tests {
159 t.Run(tt.name, func(t *testing.T) {
160 ctrl := gomock.NewController(t)
161 kubeClient := tt.fields.kubeClient(ctrl)
162 execClient := exec.NewExecClient(kubeClient)
163 do := NewDeleteComponentClient(kubeClient, nil, execClient, nil)
164 ctx := odocontext.WithApplication(context.TODO(), "app")
165 got, err := do.ListClusterResourcesToDelete(ctx, tt.args.componentName, tt.args.namespace, tt.args.mode)
166 if (err != nil) != tt.wantErr {
167 t.Errorf("DeleteComponentClient.ListResourcesToDelete() error = %v, wantErr %v", err, tt.wantErr)
168 return
169 }
170 if diff := cmp.Diff(tt.want, got); diff != "" {
171 t.Errorf("DeleteComponentClient.ListClusterResourcesToDelete() mismatch (-want +got):\n%s", diff)
172 }
173 })
174 }
175 }
176
177 func TestDeleteComponentClient_DeleteResources(t *testing.T) {
178 res1 := getUnstructured("dep1", "deployment", "v1", "")
179 res2 := getUnstructured("svc1", "service", "v1", "")
180
181 type fields struct {
182 kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface
183 }
184 type args struct {
185 resources []unstructured.Unstructured
186 }
187 tests := []struct {
188 name string
189 fields fields
190 args args
191 want []unstructured.Unstructured
192 }{
193 {
194 name: "2 resources deleted succesfully",
195 fields: fields{
196 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
197 client := kclient.NewMockClientInterface(ctrl)
198 client.EXPECT().GetRestMappingFromUnstructured(res1).Return(&meta.RESTMapping{
199 Resource: schema.GroupVersionResource{
200 Group: "",
201 Version: "v1",
202 Resource: res1.GetKind(),
203 },
204 }, nil)
205 client.EXPECT().GetRestMappingFromUnstructured(res2).Return(&meta.RESTMapping{
206 Resource: schema.GroupVersionResource{
207 Group: "",
208 Version: "v1",
209 Resource: res2.GetKind(),
210 },
211 }, nil)
212 client.EXPECT().DeleteDynamicResource(res1.GetName(), getGVR("", "v1", res1.GetKind()), false)
213 client.EXPECT().DeleteDynamicResource(res2.GetName(), getGVR("", "v1", res2.GetKind()), false)
214 return client
215 },
216 },
217 args: args{
218 resources: []unstructured.Unstructured{res1, res2},
219 },
220 want: nil,
221 },
222 {
223 name: "2 resources, 1 deleted succesfully, 1 failed during restmapping",
224 fields: fields{
225 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
226 client := kclient.NewMockClientInterface(ctrl)
227 client.EXPECT().GetRestMappingFromUnstructured(res1).Return(nil, errors.New("some restmapping error"))
228 client.EXPECT().GetRestMappingFromUnstructured(res2).Return(&meta.RESTMapping{
229 Resource: schema.GroupVersionResource{
230 Group: "",
231 Version: "v1",
232 Resource: res2.GetKind(),
233 },
234 }, nil)
235 client.EXPECT().DeleteDynamicResource(res2.GetName(), getGVR("", "v1", res2.GetKind()), false)
236 return client
237 },
238 },
239 args: args{
240 resources: []unstructured.Unstructured{res1, res2},
241 },
242 want: []unstructured.Unstructured{res1},
243 },
244 {
245 name: "2 resources, 1 deleted succesfully, 1 failed during deletion",
246 fields: fields{
247 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
248 client := kclient.NewMockClientInterface(ctrl)
249 client.EXPECT().GetRestMappingFromUnstructured(res1).Return(&meta.RESTMapping{
250 Resource: schema.GroupVersionResource{
251 Group: "",
252 Version: "v1",
253 Resource: res1.GetKind(),
254 },
255 }, nil)
256 client.EXPECT().GetRestMappingFromUnstructured(res2).Return(&meta.RESTMapping{
257 Resource: schema.GroupVersionResource{
258 Group: "",
259 Version: "v1",
260 Resource: res2.GetKind(),
261 },
262 }, nil)
263 client.EXPECT().DeleteDynamicResource(res1.GetName(), getGVR("", "v1", res1.GetKind()), false).Return(errors.New("some error"))
264 client.EXPECT().DeleteDynamicResource(res2.GetName(), getGVR("", "v1", res2.GetKind()), false)
265 return client
266 },
267 },
268 args: args{
269 resources: []unstructured.Unstructured{res1, res2},
270 },
271 want: []unstructured.Unstructured{res1},
272 },
273
274 }
275 for _, tt := range tests {
276 t.Run(tt.name, func(t *testing.T) {
277 ctrl := gomock.NewController(t)
278 kubeClient := tt.fields.kubeClient(ctrl)
279 execClient := exec.NewExecClient(kubeClient)
280 do := NewDeleteComponentClient(kubeClient, nil, execClient, nil)
281 got := do.DeleteResources(tt.args.resources, false)
282 if diff := cmp.Diff(tt.want, got); diff != "" {
283 t.Errorf("DeleteComponentClient.DeleteResources() mismatch (-want +got):\n%s", diff)
284 }
285 })
286 }
287 }
288
289 func TestDeleteComponentClient_ListClusterResourcesToDeleteFromDevfile(t *testing.T) {
290 const compName = "nodejs-prj1-api-abhz"
291 innerLoopCoreDeploymentName, _ := util.NamespaceKubernetesObject(compName, appName)
292
293
294 innerLoopCoreDeployment := odoTestingUtil.CreateFakeDeployment(compName, true)
295
296 innerLoopCoreDeploymentUnstructured, e := kclient.ConvertK8sResourceToUnstructured(innerLoopCoreDeployment)
297 if e != nil {
298 t.Errorf("unable to convert deployment to unstructured")
299 }
300
301
302 outerLoopResourceUnstructured := unstructured.Unstructured{
303 Object: map[string]interface{}{
304 "apiVersion": "apps/v1",
305 "kind": "Deployment",
306 "metadata": map[string]interface{}{
307 "name": "my-component",
308 },
309 "spec": map[string]interface{}{
310 "replicas": float64(1),
311 "selector": map[string]interface{}{
312 "matchLabels": map[string]interface{}{
313 "app": "node-app",
314 },
315 },
316 "template": map[string]interface{}{
317 "metadata": map[string]interface{}{
318 "labels": map[string]interface{}{
319 "app": "node-app",
320 },
321 },
322 "spec": map[string]interface{}{
323 "containers": []interface{}{
324 map[string]interface{}{
325 "image": "quay.io/unknown-account/myimage",
326 "name": "main",
327 "resources": map[string]interface{}{
328 "limits": map[string]interface{}{
329 "cpu": "500m",
330 "memory": "128Mi",
331 },
332 },
333 },
334 },
335 },
336 },
337 },
338 },
339 }
340
341
342 labeledOuterloopResource := *outerLoopResourceUnstructured.DeepCopy()
343 labeledOuterloopResource.SetLabels(odolabels.GetLabels(compName, appName, "", odolabels.ComponentDeployMode, false))
344
345
346 innerLoopResourceUnstructured := *outerLoopResourceUnstructured.DeepCopy()
347 innerLoopResourceUnstructured.SetLabels(odolabels.GetLabels(compName, appName, "", odolabels.ComponentDevMode, false))
348
349 deploymentRESTMapping := meta.RESTMapping{
350 Resource: getGVR("apps", "v1", "Deployment"),
351 }
352
353 type fields struct {
354 kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface
355 }
356 type args struct {
357 devfileObj parser.DevfileObj
358 appName string
359 mode string
360 }
361 tests := []struct {
362 name string
363 fields fields
364 args args
365 wantIsInnerLoopDeployed bool
366 wantResources []unstructured.Unstructured
367 wantErr bool
368 }{
369 {
370 name: "list innerloop core resource(deployment), and outerloop resources",
371 fields: fields{
372 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
373 kubeClient := kclient.NewMockClientInterface(ctrl)
374
375 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(innerLoopCoreDeployment, nil)
376
377 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(&deploymentRESTMapping, nil)
378 kubeClient.EXPECT().
379 GetDynamicResource(deploymentRESTMapping.Resource, outerLoopResourceUnstructured.GetName()).
380 Return(&labeledOuterloopResource, nil)
381
382 return kubeClient
383 },
384 },
385 args: args{
386 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy.yaml"),
387 appName: appName,
388 mode: odolabels.ComponentAnyMode,
389 },
390 wantIsInnerLoopDeployed: true,
391 wantResources: []unstructured.Unstructured{innerLoopCoreDeploymentUnstructured, labeledOuterloopResource},
392 wantErr: false,
393 },
394 {
395 name: "list innerloop core resource(deployment) only",
396 fields: fields{
397 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
398 kubeClient := kclient.NewMockClientInterface(ctrl)
399
400 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(innerLoopCoreDeployment, nil)
401 return kubeClient
402 },
403 },
404 args: args{
405 devfileObj: func() parser.DevfileObj {
406 obj := odoTestingUtil.GetTestDevfileObjFromFile("devfile.yaml")
407
408 metadata := obj.Data.GetMetadata()
409 metadata.Name = compName
410 obj.Data.SetMetadata(metadata)
411 return obj
412 }(),
413 appName: appName,
414 mode: odolabels.ComponentDevMode,
415 },
416 wantIsInnerLoopDeployed: true,
417 wantResources: []unstructured.Unstructured{innerLoopCoreDeploymentUnstructured},
418 wantErr: false,
419 },
420 {
421 name: "list innerloop core resources(deployment), another innerloop resources",
422 fields: fields{
423 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
424 kubeClient := kclient.NewMockClientInterface(ctrl)
425
426 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(innerLoopCoreDeployment, nil)
427
428 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(&deploymentRESTMapping, nil)
429 kubeClient.EXPECT().
430 GetDynamicResource(deploymentRESTMapping.Resource, outerLoopResourceUnstructured.GetName()).
431 Return(&innerLoopResourceUnstructured, nil)
432
433 return kubeClient
434 },
435 },
436 args: args{
437 devfileObj: func() parser.DevfileObj {
438 obj := odoTestingUtil.GetTestDevfileObjFromFile("devfile-composite-apply-commands-unit-test.yaml")
439
440 metadata := obj.Data.GetMetadata()
441 metadata.Name = compName
442 obj.Data.SetMetadata(metadata)
443 return obj
444 }(),
445 appName: appName,
446 mode: odolabels.ComponentDevMode,
447 },
448 wantIsInnerLoopDeployed: true,
449 wantResources: []unstructured.Unstructured{innerLoopCoreDeploymentUnstructured, innerLoopResourceUnstructured},
450 wantErr: false,
451 },
452 {
453 name: "list outerloop resources only",
454 fields: fields{
455 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
456 kubeClient := kclient.NewMockClientInterface(ctrl)
457
458 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).
459 Return(&appsv1.Deployment{}, kerrors.NewNotFound(deploymentRESTMapping.Resource.GroupResource(), innerLoopCoreDeploymentName))
460 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(&deploymentRESTMapping, nil)
461 kubeClient.EXPECT().
462 GetDynamicResource(deploymentRESTMapping.Resource, outerLoopResourceUnstructured.GetName()).
463 Return(&labeledOuterloopResource, nil)
464 return kubeClient
465 },
466 },
467 args: args{
468 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy.yaml"),
469 appName: appName,
470 mode: odolabels.ComponentAnyMode,
471 },
472 wantIsInnerLoopDeployed: false,
473 wantResources: []unstructured.Unstructured{labeledOuterloopResource},
474 wantErr: false,
475 },
476 {
477 name: "list uri-referenced outerloop resources",
478 fields: fields{
479 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
480 kubeClient := kclient.NewMockClientInterface(ctrl)
481 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).
482 Return(&appsv1.Deployment{}, kerrors.NewNotFound(schema.GroupResource{Group: "apps", Resource: "Deployments"}, innerLoopCoreDeploymentName))
483 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(&deploymentRESTMapping, nil)
484 kubeClient.EXPECT().
485 GetDynamicResource(deploymentRESTMapping.Resource, outerLoopResourceUnstructured.GetName()).
486 Return(&labeledOuterloopResource, nil)
487 return kubeClient
488 },
489 },
490 args: args{
491 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy-with-k8s-uri.yaml"),
492 appName: appName,
493 mode: odolabels.ComponentAnyMode,
494 },
495 wantIsInnerLoopDeployed: false,
496 wantResources: []unstructured.Unstructured{labeledOuterloopResource},
497 wantErr: false,
498 },
499 {
500 name: "fetching inner loop resource failed due to some error(!NotFoundError)",
501 fields: fields{
502 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
503 kubeClient := kclient.NewMockClientInterface(ctrl)
504 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(&appsv1.Deployment{}, errors.New("some error"))
505 return kubeClient
506 },
507 },
508 args: args{
509 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy.yaml"),
510 appName: appName,
511 mode: odolabels.ComponentAnyMode,
512 },
513 wantIsInnerLoopDeployed: false,
514 wantResources: nil,
515 wantErr: true,
516 },
517 {
518 name: "failed to add outerloop resource to the list because kubeclient.GetRestMappingFromUnstructured() failed",
519 fields: fields{
520 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
521 kubeClient := kclient.NewMockClientInterface(ctrl)
522 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(innerLoopCoreDeployment, nil)
523 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(nil, errors.New("some error"))
524 return kubeClient
525 },
526 },
527 args: args{
528 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy.yaml"),
529 appName: appName,
530 mode: odolabels.ComponentAnyMode,
531 },
532 wantIsInnerLoopDeployed: true,
533 wantResources: []unstructured.Unstructured{innerLoopCoreDeploymentUnstructured},
534 wantErr: false,
535 },
536 {
537 name: "failed to add outerloop resource to the list because kubeclient.GetDynamicResource() failed",
538 fields: fields{
539 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
540 kubeClient := kclient.NewMockClientInterface(ctrl)
541 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(innerLoopCoreDeployment, nil)
542 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(&deploymentRESTMapping, nil)
543 kubeClient.EXPECT().
544 GetDynamicResource(deploymentRESTMapping.Resource, outerLoopResourceUnstructured.GetName()).
545 Return(nil, errors.New("some error"))
546 return kubeClient
547 },
548 },
549 args: args{
550 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy.yaml"),
551 appName: appName,
552 mode: odolabels.ComponentAnyMode,
553 },
554 wantIsInnerLoopDeployed: true,
555 wantResources: []unstructured.Unstructured{innerLoopCoreDeploymentUnstructured},
556 wantErr: false,
557 },
558 {
559 name: "do not list outerloop resource if Dev mode is asked",
560 fields: fields{
561 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
562 kubeClient := kclient.NewMockClientInterface(ctrl)
563
564 kubeClient.EXPECT().GetDeploymentByName(innerLoopCoreDeploymentName).Return(innerLoopCoreDeployment, nil)
565
566 kubeClient.EXPECT().GetRestMappingFromUnstructured(outerLoopResourceUnstructured).Return(&deploymentRESTMapping, nil)
567 kubeClient.EXPECT().
568 GetDynamicResource(deploymentRESTMapping.Resource, outerLoopResourceUnstructured.GetName()).
569 Return(&outerLoopResourceUnstructured, nil)
570
571 return kubeClient
572 },
573 },
574 args: args{
575 devfileObj: func() parser.DevfileObj {
576 obj := odoTestingUtil.GetTestDevfileObjFromFile("devfile-composite-apply-commands-unit-test.yaml")
577
578 metadata := obj.Data.GetMetadata()
579 metadata.Name = compName
580 obj.Data.SetMetadata(metadata)
581 return obj
582 }(),
583 appName: appName,
584 mode: odolabels.ComponentDevMode,
585 },
586 wantIsInnerLoopDeployed: true,
587 wantResources: []unstructured.Unstructured{innerLoopCoreDeploymentUnstructured},
588 wantErr: false,
589 },
590 }
591 for _, tt := range tests {
592 t.Run(tt.name, func(t *testing.T) {
593 ctrl := gomock.NewController(t)
594 do := DeleteComponentClient{
595 kubeClient: tt.fields.kubeClient(ctrl),
596 }
597 gotIsInnerLoopDeployed, gotResources, err := do.ListClusterResourcesToDeleteFromDevfile(tt.args.devfileObj, tt.args.appName, tt.args.devfileObj.GetMetadataName(), tt.args.mode)
598 if (err != nil) != tt.wantErr {
599 t.Errorf("ListResourcesToDeleteFromDevfile() error = %v, wantErr %v", err, tt.wantErr)
600 return
601 }
602 if gotIsInnerLoopDeployed != tt.wantIsInnerLoopDeployed {
603 t.Errorf("ListResourcesToDeleteFromDevfile() gotIsInnerLoopDeployed = %v, want %v", gotIsInnerLoopDeployed, tt.wantIsInnerLoopDeployed)
604 }
605 if diff := cmp.Diff(tt.wantResources, gotResources); diff != "" {
606 t.Errorf("DeleteComponentClient.ListResourcesToDeleteFromDevfile() wantResources mismatch (-want +got):\n%s", diff)
607 }
608 })
609 }
610 }
611
612 func TestDeleteComponentClient_ExecutePreStopEvents(t *testing.T) {
613 const componentName = "nodejs-prj1-api-abhz"
614 const appName = "app"
615 fs := filesystem.NewFakeFs()
616
617 devfileObjWithPreStopEvents := odoTestingUtil.GetTestDevfileObjWithPreStopEvents(fs, "runtime", "echo \"Hello World!\"")
618 metadata := devfileObjWithPreStopEvents.Data.GetMetadata()
619 metadata.Name = componentName
620 devfileObjWithPreStopEvents.Data.SetMetadata(metadata)
621
622 type fields struct {
623 kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface
624 }
625 type args struct {
626 devfileObj parser.DevfileObj
627 appName string
628 }
629 tests := []struct {
630 name string
631 fields fields
632 args args
633 wantErr bool
634 }{
635 {
636 name: "no preStop event to execute",
637 fields: fields{
638 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
639 return kclient.NewMockClientInterface(ctrl)
640 },
641 },
642 args: args{
643 devfileObj: odoTestingUtil.GetTestDevfileObjFromFile("devfile-deploy.yaml"),
644 appName: appName,
645 },
646 wantErr: false,
647 },
648 {
649 name: "did not execute preStop event because pod was not found",
650 fields: fields{
651 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
652 client := kclient.NewMockClientInterface(ctrl)
653
654 selector := odolabels.GetSelector(componentName, "app", odolabels.ComponentDevMode, false)
655 client.EXPECT().GetRunningPodFromSelector(selector).Return(&corev1.Pod{}, &platform.PodNotFoundError{Selector: selector})
656 return client
657 },
658 },
659 args: args{
660 devfileObj: devfileObjWithPreStopEvents,
661 appName: appName,
662 },
663 wantErr: false,
664 },
665 {
666 name: "failed to execute preStop event because of an un-ignorable error",
667 fields: fields{
668 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
669 client := kclient.NewMockClientInterface(ctrl)
670
671 selector := odolabels.GetSelector(componentName, "app", odolabels.ComponentDevMode, false)
672 client.EXPECT().GetRunningPodFromSelector(selector).Return(nil, errors.New("some un-ignorable error"))
673 return client
674 },
675 },
676 args: args{
677 devfileObj: devfileObjWithPreStopEvents,
678 appName: appName,
679 },
680 wantErr: true,
681 },
682 {
683 name: "successfully executed preStop events in the running pod",
684 fields: fields{
685 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
686 client := kclient.NewMockClientInterface(ctrl)
687
688 selector := odolabels.GetSelector(componentName, "app", odolabels.ComponentDevMode, false)
689 client.EXPECT().GetRunningPodFromSelector(selector).Return(odoTestingUtil.CreateFakePod(componentName, "mypod", "runtime"), nil)
690
691 cmd := []string{"/bin/sh", "-c", "cd /projects/nodejs-starter && (echo \"Hello World!\") 1>>/proc/1/fd/1 2>>/proc/1/fd/2"}
692 client.EXPECT().ExecCMDInContainer(gomock.Any(), "runtime", "mypod", cmd, gomock.Any(), gomock.Any(), nil, false).Return(nil)
693
694 return client
695 },
696 },
697 args: args{
698 devfileObj: devfileObjWithPreStopEvents,
699 appName: appName,
700 },
701 wantErr: false,
702 },
703 {
704 name: "failed to execute PreStopEvents because it failed to execute the command inside the container, but no error returned",
705 fields: fields{
706 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
707 client := kclient.NewMockClientInterface(ctrl)
708
709 selector := odolabels.GetSelector(componentName, "app", odolabels.ComponentDevMode, false)
710 fakePod := odoTestingUtil.CreateFakePod(componentName, "mypod", "runtime")
711
712 client.EXPECT().GetRunningPodFromSelector(selector).Return(fakePod, nil).Times(2)
713
714 client.EXPECT().GetPodLogs(fakePod.Name, gomock.Any(), gomock.Any()).Return(nil, errors.New("an error"))
715
716 cmd := []string{"/bin/sh", "-c", "cd /projects/nodejs-starter && (echo \"Hello World!\") 1>>/proc/1/fd/1 2>>/proc/1/fd/2"}
717 client.EXPECT().ExecCMDInContainer(gomock.Any(), "runtime", "mypod", cmd, gomock.Any(), gomock.Any(), nil, false).Return(errors.New("some error"))
718
719 return client
720 },
721 },
722 args: args{
723 devfileObj: devfileObjWithPreStopEvents,
724 appName: appName,
725 },
726 wantErr: false,
727 },
728 }
729 for _, tt := range tests {
730 t.Run(tt.name, func(t *testing.T) {
731 ctrl := gomock.NewController(t)
732 kubeClient := tt.fields.kubeClient(ctrl)
733 execClient := exec.NewExecClient(kubeClient)
734 do := NewDeleteComponentClient(kubeClient, nil, execClient, nil)
735 ctx := context.Background()
736 ctx = odocontext.WithApplication(ctx, appName)
737 ctx = odocontext.WithComponentName(ctx, componentName)
738 if err := do.ExecutePreStopEvents(ctx, tt.args.devfileObj, tt.args.appName, tt.args.devfileObj.GetMetadataName()); (err != nil) != tt.wantErr {
739 t.Errorf("DeleteComponent() error = %v, wantErr %v", err, tt.wantErr)
740 }
741 })
742 }
743 }
744
745
746 func getUnstructured(name, kind, apiVersion, namespace string) (u unstructured.Unstructured) {
747 u.SetName(name)
748 u.SetKind(kind)
749 u.SetAPIVersion(apiVersion)
750 u.SetNamespace(namespace)
751 return
752 }
753
754 func getGVR(group, version, resource string) schema.GroupVersionResource {
755 return schema.GroupVersionResource{
756 Group: group,
757 Version: version,
758 Resource: resource,
759 }
760 }
761
762 func TestDeleteComponentClient_ListPodmanResourcesToDeleteFromDevfile(t *testing.T) {
763 type fields struct {
764 podmanClient func(ctrl *gomock.Controller) podman.Client
765 }
766 type args struct {
767 appName string
768 componentName string
769 mode string
770 }
771
772 podName := "a-component-an-app"
773 podDef := corev1.Pod{}
774 podDef.SetName(podName)
775
776 tests := []struct {
777 name string
778 fields fields
779 args args
780 wantIsInnerLoopDeployed bool
781 wantPods []*corev1.Pod
782 wantErr bool
783 }{
784 {
785 name: "blank name",
786 fields: fields{
787 podmanClient: func(ctrl *gomock.Controller) podman.Client {
788 return podman.NewMockClient(ctrl)
789 },
790 },
791 wantErr: true,
792 },
793 {
794 name: "error get pods on podman",
795 fields: fields{
796 podmanClient: func(ctrl *gomock.Controller) podman.Client {
797 podmanCli := podman.NewMockClient(ctrl)
798 podmanCli.EXPECT().PodLs().Return(nil, errors.New("error running PodLs"))
799 return podmanCli
800 },
801 },
802 args: args{
803 appName: "an-app",
804 componentName: "a-component",
805 },
806 wantErr: true,
807 },
808 {
809 name: "no pod running on podman",
810 fields: fields{
811 podmanClient: func(ctrl *gomock.Controller) podman.Client {
812 podmanCli := podman.NewMockClient(ctrl)
813 podmanCli.EXPECT().PodLs().Return(map[string]bool{}, nil)
814 return podmanCli
815 },
816 },
817 args: args{
818 appName: "an-app",
819 componentName: "a-component",
820 },
821 wantErr: false,
822 wantIsInnerLoopDeployed: false,
823 wantPods: nil,
824 },
825 {
826 name: "another pod running on podman",
827 fields: fields{
828 podmanClient: func(ctrl *gomock.Controller) podman.Client {
829 podmanCli := podman.NewMockClient(ctrl)
830 podmanCli.EXPECT().PodLs().Return(map[string]bool{"another-pod": true}, nil)
831 return podmanCli
832 },
833 },
834 args: args{
835 appName: "an-app",
836 componentName: "a-component",
837 },
838 wantErr: false,
839 wantIsInnerLoopDeployed: false,
840 wantPods: nil,
841 },
842 {
843 name: "component's pod running on podman",
844 fields: fields{
845 podmanClient: func(ctrl *gomock.Controller) podman.Client {
846 podmanCli := podman.NewMockClient(ctrl)
847 podmanCli.EXPECT().PodLs().Return(map[string]bool{podName: true}, nil)
848
849 podmanCli.EXPECT().KubeGenerate(podName).Return(&podDef, nil)
850 return podmanCli
851 },
852 },
853 args: args{
854 appName: "an-app",
855 componentName: "a-component",
856 },
857 wantErr: false,
858 wantIsInnerLoopDeployed: true,
859 wantPods: []*corev1.Pod{&podDef},
860 },
861 {
862 name: "component's pod running on podman - dev mode requested",
863 fields: fields{
864 podmanClient: func(ctrl *gomock.Controller) podman.Client {
865 podmanCli := podman.NewMockClient(ctrl)
866 podmanCli.EXPECT().PodLs().Return(map[string]bool{podName: true}, nil)
867
868 podmanCli.EXPECT().KubeGenerate(podName).Return(&podDef, nil)
869 return podmanCli
870 },
871 },
872 args: args{
873 appName: "an-app",
874 componentName: "a-component",
875 mode: odolabels.ComponentDevMode,
876 },
877 wantErr: false,
878 wantIsInnerLoopDeployed: true,
879 wantPods: []*corev1.Pod{&podDef},
880 },
881 {
882 name: "component's pod running on podman - deploy mode requested",
883 fields: fields{
884 podmanClient: func(ctrl *gomock.Controller) podman.Client {
885 podmanCli := podman.NewMockClient(ctrl)
886 podmanCli.EXPECT().PodLs().Return(map[string]bool{podName: true}, nil).Times(0)
887
888 podmanCli.EXPECT().KubeGenerate(podName).Return(&podDef, nil).Times(0)
889 return podmanCli
890 },
891 },
892 args: args{
893 appName: "an-app",
894 componentName: "a-component",
895 mode: odolabels.ComponentDeployMode,
896 },
897 wantErr: false,
898 wantIsInnerLoopDeployed: false,
899 wantPods: nil,
900 },
901 {
902 name: "kube generate fails",
903 fields: fields{
904 podmanClient: func(ctrl *gomock.Controller) podman.Client {
905 podmanCli := podman.NewMockClient(ctrl)
906 podmanCli.EXPECT().PodLs().Return(map[string]bool{podName: true}, nil)
907
908 podmanCli.EXPECT().KubeGenerate(podName).Return(nil, errors.New("error executing KubeGenerate"))
909 return podmanCli
910 },
911 },
912 args: args{
913 appName: "an-app",
914 componentName: "a-component",
915 },
916 wantErr: true,
917 },
918 }
919 for _, tt := range tests {
920 t.Run(tt.name, func(t *testing.T) {
921 ctrl := gomock.NewController(t)
922 do := &DeleteComponentClient{
923 podmanClient: tt.fields.podmanClient(ctrl),
924 }
925 gotIsInnerLoopDeployed, gotPods, err := do.ListPodmanResourcesToDelete(tt.args.appName, tt.args.componentName, tt.args.mode)
926 if (err != nil) != tt.wantErr {
927 t.Errorf("DeleteComponentClient.ListPodmanResourcesToDeleteFromDevfile() error = %v, wantErr %v", err, tt.wantErr)
928 return
929 }
930 if gotIsInnerLoopDeployed != tt.wantIsInnerLoopDeployed {
931 t.Errorf("DeleteComponentClient.ListPodmanResourcesToDeleteFromDevfile() gotIsInnerLoopDeployed = %v, want %v", gotIsInnerLoopDeployed, tt.wantIsInnerLoopDeployed)
932 }
933 if !reflect.DeepEqual(gotPods, tt.wantPods) {
934 t.Errorf("DeleteComponentClient.ListPodmanResourcesToDeleteFromDevfile() gotPods = %v, want %v", gotPods, tt.wantPods)
935 }
936 })
937 }
938 }
939
View as plain text