1 package component
2
3 import (
4 "context"
5 "errors"
6 "os"
7 "path"
8 "path/filepath"
9 "testing"
10
11 devfilepkg "github.com/devfile/api/v2/pkg/devfile"
12 "github.com/devfile/library/v2/pkg/devfile/parser"
13 devfileCtx "github.com/devfile/library/v2/pkg/devfile/parser/context"
14 "github.com/devfile/library/v2/pkg/devfile/parser/data"
15 "github.com/devfile/library/v2/pkg/testingutil/filesystem"
16 dfutil "github.com/devfile/library/v2/pkg/util"
17 "github.com/golang/mock/gomock"
18 "github.com/google/go-cmp/cmp"
19 v12 "github.com/openshift/api/route/v1"
20 v1 "k8s.io/api/networking/v1"
21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23 "k8s.io/apimachinery/pkg/runtime"
24 "k8s.io/apimachinery/pkg/runtime/schema"
25
26 "github.com/redhat-developer/odo/pkg/devfile"
27 "github.com/redhat-developer/odo/pkg/kclient"
28 "github.com/redhat-developer/odo/pkg/labels"
29 "github.com/redhat-developer/odo/pkg/libdevfile"
30 odocontext "github.com/redhat-developer/odo/pkg/odo/context"
31 "github.com/redhat-developer/odo/pkg/platform"
32 "github.com/redhat-developer/odo/pkg/podman"
33 "github.com/redhat-developer/odo/pkg/testingutil"
34 "github.com/redhat-developer/odo/pkg/util"
35
36 "github.com/redhat-developer/odo/pkg/api"
37 )
38
39 func TestListAllClusterComponents(t *testing.T) {
40 const odoVersion = "v3.0.0-beta3"
41 res1 := getUnstructured("dep1", "deployment", "v1", "Unknown", "", "Unknown", "my-ns")
42 res2 := getUnstructured("svc1", "service", "v1", "odo", odoVersion, "nodejs", "my-ns")
43 res3 := getUnstructured("dep1", "deployment", "v1", "Unknown", "", "Unknown", "my-ns")
44 res3.SetLabels(map[string]string{})
45
46 commonLabels := labels.Builder().WithComponentName("comp1").WithManager("odo").WithManagedByVersion(odoVersion)
47
48 resDev := getUnstructured("depDev", "deployment", "v1", "odo", odoVersion, "nodejs", "my-ns")
49 labelsDev := commonLabels.WithMode("Dev").Labels()
50 resDev.SetLabels(labelsDev)
51
52 resDeploy := getUnstructured("depDeploy", "deployment", "v1", "odo", odoVersion, "nodejs", "my-ns")
53 labelsDeploy := commonLabels.WithMode("Deploy").Labels()
54 resDeploy.SetLabels(labelsDeploy)
55
56 type fields struct {
57 kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface
58 }
59 type args struct {
60 namespace string
61 }
62 tests := []struct {
63 name string
64 fields fields
65 args args
66 want []api.ComponentAbstract
67 wantErr bool
68 }{
69 {
70 name: "1 non-odo resource returned with Unknown",
71 fields: fields{
72 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
73 var resources []unstructured.Unstructured
74 resources = append(resources, res1)
75 client := kclient.NewMockClientInterface(ctrl)
76 selector := ""
77 client.EXPECT().GetAllResourcesFromSelector(selector, "my-ns").Return(resources, nil)
78 return client
79 },
80 },
81 args: args{
82 namespace: "my-ns",
83 },
84 want: []api.ComponentAbstract{{
85 Name: "dep1",
86 ManagedBy: "Unknown",
87 ManagedByVersion: "",
88 RunningIn: nil,
89 Type: "Unknown",
90 RunningOn: "cluster",
91 Platform: "cluster",
92 }},
93 wantErr: false,
94 },
95 {
96 name: "0 non-odo resource without instance label is not returned",
97 fields: fields{
98 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
99 var resources []unstructured.Unstructured
100 resources = append(resources, res3)
101 client := kclient.NewMockClientInterface(ctrl)
102 client.EXPECT().GetAllResourcesFromSelector(gomock.Any(), "my-ns").Return(resources, nil)
103 return client
104 },
105 },
106 args: args{
107 namespace: "my-ns",
108 },
109 want: nil,
110 wantErr: false,
111 },
112 {
113 name: "1 non-odo resource returned with Unknown, and 1 odo resource returned with odo",
114 fields: fields{
115 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
116 var resources []unstructured.Unstructured
117 resources = append(resources, res1, res2)
118 client := kclient.NewMockClientInterface(ctrl)
119 client.EXPECT().GetAllResourcesFromSelector(gomock.Any(), "my-ns").Return(resources, nil)
120 return client
121 },
122 },
123 args: args{
124 namespace: "my-ns",
125 },
126 want: []api.ComponentAbstract{{
127 Name: "dep1",
128 ManagedBy: "Unknown",
129 ManagedByVersion: "",
130 RunningIn: nil,
131 Type: "Unknown",
132 RunningOn: "cluster",
133 Platform: "cluster",
134 }, {
135 Name: "svc1",
136 ManagedBy: "odo",
137 ManagedByVersion: "v3.0.0-beta3",
138 RunningIn: nil,
139 Type: "nodejs",
140 RunningOn: "cluster",
141 Platform: "cluster",
142 }},
143 wantErr: false,
144 },
145 {
146 name: "one resource in Dev and Deploy modes",
147 fields: fields{
148 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
149 var resources []unstructured.Unstructured
150 resources = append(resources, resDev, resDeploy)
151 client := kclient.NewMockClientInterface(ctrl)
152 client.EXPECT().GetAllResourcesFromSelector(gomock.Any(), "my-ns").Return(resources, nil)
153 return client
154 },
155 },
156 args: args{
157 namespace: "my-ns",
158 },
159 want: []api.ComponentAbstract{{
160 Name: "comp1",
161 ManagedBy: "odo",
162 ManagedByVersion: "v3.0.0-beta3",
163 RunningIn: api.RunningModes{
164 "dev": true,
165 "deploy": true,
166 },
167 Type: "nodejs",
168 RunningOn: "cluster",
169 Platform: "cluster",
170 }},
171 wantErr: false,
172 },
173 }
174 for _, tt := range tests {
175 t.Run(tt.name, func(t *testing.T) {
176 ctrl := gomock.NewController(t)
177 got, err := ListAllClusterComponents(tt.fields.kubeClient(ctrl), tt.args.namespace)
178 if (err != nil) != tt.wantErr {
179 t.Errorf("ListAllClusterComponents error = %v, wantErr %v", err, tt.wantErr)
180 return
181 }
182 if diff := cmp.Diff(tt.want, got); diff != "" {
183 t.Errorf("ListAllClusterComponents() mismatch (-want +got):\n%s", diff)
184 }
185 })
186 }
187 }
188
189 func TestGetComponentTypeFromDevfileMetadata(t *testing.T) {
190 tests := []devfilepkg.DevfileMetadata{
191 {
192 Name: "ReturnProject",
193 ProjectType: "Maven",
194 Language: "Java",
195 },
196 {
197 Name: "ReturnLanguage",
198 Language: "Java",
199 },
200 {
201 Name: "ReturnNA",
202 },
203 }
204 for _, tt := range tests {
205 t.Run(tt.Name, func(t *testing.T) {
206 var want string
207 got := GetComponentTypeFromDevfileMetadata(tt)
208 switch tt.Name {
209 case "ReturnProject":
210 want = tt.ProjectType
211 case "ReturnLanguage":
212 want = tt.Language
213 case "ReturnNA":
214 want = NotAvailable
215 }
216 if got != want {
217 t.Errorf("Incorrect component type returned; got: %q, want: %q", got, want)
218 }
219 })
220 }
221 }
222
223
224 func getUnstructured(name, kind, apiVersion, managed, managedByVersion, componentType, namespace string) (u unstructured.Unstructured) {
225 u.SetName(name)
226 u.SetKind(kind)
227 u.SetAPIVersion(apiVersion)
228 u.SetNamespace(namespace)
229 u.SetLabels(labels.Builder().
230 WithComponentName(name).
231 WithManager(managed).
232 WithManagedByVersion(managedByVersion).
233 Labels())
234 u.SetAnnotations(labels.Builder().
235 WithProjectType(componentType).
236 Labels())
237 return
238 }
239
240 func TestGetRunningModes(t *testing.T) {
241
242 resourceDev1 := unstructured.Unstructured{}
243 resourceDev1.SetLabels(labels.Builder().WithMode(labels.ComponentDevMode).Labels())
244
245 resourceDev2 := unstructured.Unstructured{}
246 resourceDev2.SetLabels(labels.Builder().WithMode(labels.ComponentDevMode).Labels())
247
248 resourceDeploy1 := unstructured.Unstructured{}
249 resourceDeploy1.SetLabels(labels.Builder().WithMode(labels.ComponentDeployMode).Labels())
250
251 resourceDeploy2 := unstructured.Unstructured{}
252 resourceDeploy2.SetLabels(labels.Builder().WithMode(labels.ComponentDeployMode).Labels())
253
254 otherResource := unstructured.Unstructured{}
255
256 packageManifestResource := unstructured.Unstructured{}
257 packageManifestResource.SetKind("PackageManifest")
258 packageManifestResource.SetLabels(labels.Builder().WithMode(labels.ComponentDevMode).Labels())
259
260 type args struct {
261 kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface
262 podmanClient func(ctrl *gomock.Controller) podman.Client
263 name string
264 }
265 tests := []struct {
266 name string
267 args args
268 want func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes
269 wantErr bool
270 }{
271 {
272 name: "no kube client and no podman client",
273 args: args{
274 name: "aname",
275 },
276 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
277 return nil
278 },
279 wantErr: true,
280 },
281 {
282 name: "No cluster resources",
283 args: args{
284 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
285 c := kclient.NewMockClientInterface(ctrl)
286 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
287 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{}, nil)
288 return c
289 },
290 name: "aname",
291 },
292 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
293 return nil
294 },
295 wantErr: true,
296 },
297 {
298 name: "Only PackageManifest resources returned by cluster",
299 args: args{
300 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
301 c := kclient.NewMockClientInterface(ctrl)
302 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
303 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource}, nil)
304 return c
305 },
306 name: "aname",
307 },
308 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
309 return nil
310 },
311 wantErr: true,
312 },
313 {
314 name: "No dev/deploy resources",
315 args: args{
316 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
317 c := kclient.NewMockClientInterface(ctrl)
318 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
319 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(
320 []unstructured.Unstructured{packageManifestResource, otherResource}, nil)
321 return c
322 },
323 podmanClient: func(ctrl *gomock.Controller) podman.Client {
324 c := podman.NewMockClient(ctrl)
325 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(
326 []unstructured.Unstructured{packageManifestResource, otherResource}, nil)
327 return c
328 },
329 name: "aname",
330 },
331 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
332 return map[platform.Client]api.RunningModes{
333 kubeClient: {"dev": false, "deploy": false},
334 podmanClient: {"dev": false, "deploy": false},
335 }
336 },
337 },
338 {
339 name: "Only Dev cluster resources and no Podman resources",
340 args: args{
341 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
342 c := kclient.NewMockClientInterface(ctrl)
343 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
344 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource, resourceDev1, resourceDev2}, nil)
345 return c
346 },
347 podmanClient: func(ctrl *gomock.Controller) podman.Client {
348 c := podman.NewMockClient(ctrl)
349 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, nil)
350 return c
351 },
352 name: "aname",
353 },
354 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
355 return map[platform.Client]api.RunningModes{kubeClient: {"dev": true, "deploy": false}}
356 },
357 },
358 {
359 name: "Only Deploy cluster resources",
360 args: args{
361 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
362 c := kclient.NewMockClientInterface(ctrl)
363 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
364 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource, resourceDeploy1, resourceDeploy2}, nil)
365 return c
366 },
367 name: "aname",
368 },
369 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
370 return map[platform.Client]api.RunningModes{kubeClient: {"dev": false, "deploy": true}}
371 },
372 },
373 {
374 name: "Dev and Deploy cluster resources",
375 args: args{
376 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
377 c := kclient.NewMockClientInterface(ctrl)
378 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
379 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return([]unstructured.Unstructured{packageManifestResource, otherResource, resourceDev1, resourceDev2, resourceDeploy1, resourceDeploy2}, nil)
380 return c
381 },
382 name: "aname",
383 },
384 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
385 return map[platform.Client]api.RunningModes{kubeClient: {"dev": true, "deploy": true}}
386 },
387 },
388 {
389 name: "Dev and Deploy cluster resources, Dev Podman resources",
390 args: args{
391 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
392 c := kclient.NewMockClientInterface(ctrl)
393 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
394 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(
395 []unstructured.Unstructured{
396 packageManifestResource, otherResource, resourceDev1, resourceDev2, resourceDeploy1, resourceDeploy2},
397 nil)
398 return c
399 },
400 podmanClient: func(ctrl *gomock.Controller) podman.Client {
401 c := podman.NewMockClient(ctrl)
402 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(
403 []unstructured.Unstructured{resourceDev1, resourceDev2}, nil)
404 return c
405 },
406 name: "aname",
407 },
408 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
409 return map[platform.Client]api.RunningModes{
410 kubeClient: {"dev": true, "deploy": true},
411 podmanClient: {"dev": true, "deploy": false},
412 }
413 },
414 },
415 {
416 name: "Unknown",
417 args: args{
418 kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
419 c := kclient.NewMockClientInterface(ctrl)
420 c.EXPECT().GetCurrentNamespace().Return("a-namespace").AnyTimes()
421 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, errors.New("error"))
422 return c
423 },
424 name: "aname",
425 },
426 want: func(kubeClient, podmanClient platform.Client) map[platform.Client]api.RunningModes {
427 return nil
428 },
429 },
430 }
431 for _, tt := range tests {
432 t.Run(tt.name, func(t *testing.T) {
433 ctrl := gomock.NewController(t)
434 ctx := odocontext.WithApplication(context.TODO(), "app")
435 var kubeClient kclient.ClientInterface
436 if tt.args.kubeClient != nil {
437 kubeClient = tt.args.kubeClient(ctrl)
438 }
439 var podmanClient podman.Client
440 if tt.args.podmanClient != nil {
441 podmanClient = tt.args.podmanClient(ctrl)
442 }
443 got, err := GetRunningModes(ctx, kubeClient, podmanClient, tt.args.name)
444 if (err != nil) != tt.wantErr {
445 t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
446 return
447 }
448 want := tt.want(kubeClient, podmanClient)
449 if diff := cmp.Diff(want, got); diff != "" {
450 t.Errorf("GetRunningModes() mismatch (-want +got):\n%s", diff)
451 }
452 })
453 }
454 }
455
456 func TestGatherName(t *testing.T) {
457 type devfileProvider func() (*parser.DevfileObj, string, error)
458 fakeDevfileWithNameProvider := func(name string) devfileProvider {
459 return func() (*parser.DevfileObj, string, error) {
460 dData, err := data.NewDevfileData(string(data.APISchemaVersion220))
461 if err != nil {
462 return nil, "", err
463 }
464 dData.SetMetadata(devfilepkg.DevfileMetadata{Name: name})
465 return &parser.DevfileObj{
466 Ctx: devfileCtx.FakeContext(filesystem.NewFakeFs(), parser.OutputDevfileYamlPath),
467 Data: dData,
468 }, "", nil
469 }
470 }
471
472 fs := filesystem.DefaultFs{}
473
474
475 realDevfileWithNameProvider := func(name string) devfileProvider {
476 return func() (*parser.DevfileObj, string, error) {
477 dir, err := fs.TempDir("", "Component_GatherName_")
478 if err != nil {
479 return nil, dir, err
480 }
481
482 originalDevfile := testingutil.GetTestDevfileObjFromFile("devfile.yaml")
483 originalDevfilePath := originalDevfile.Ctx.GetAbsPath()
484
485 stat, err := os.Stat(originalDevfilePath)
486 if err != nil {
487 return nil, dir, err
488 }
489 dPath := path.Join(dir, "devfile.yaml")
490 err = dfutil.CopyFile(originalDevfilePath, dPath, stat)
491 if err != nil {
492 return nil, dir, err
493 }
494
495 d, err := devfile.ParseAndValidateFromFile(dPath, "", false)
496 if err != nil {
497 return nil, dir, err
498 }
499
500 err = d.SetMetadataName(name)
501
502 return &d, dir, err
503 }
504 }
505
506 wantDevfileDirectoryName := func(contextDir string, d *parser.DevfileObj) string {
507 return util.GetDNS1123Name(filepath.Base(filepath.Dir(d.Ctx.GetAbsPath())))
508 }
509
510 for _, tt := range []struct {
511 name string
512 devfileProviderFunc devfileProvider
513 wantErr bool
514 want func(contextDir string, d *parser.DevfileObj) string
515 }{
516 {
517 name: "compliant name",
518 devfileProviderFunc: fakeDevfileWithNameProvider("my-component-name"),
519 want: func(contextDir string, d *parser.DevfileObj) string { return "my-component-name" },
520 },
521 {
522 name: "un-sanitized name",
523 devfileProviderFunc: fakeDevfileWithNameProvider("name with spaces"),
524 want: func(contextDir string, d *parser.DevfileObj) string { return "name-with-spaces" },
525 },
526 {
527 name: "all numeric name",
528 devfileProviderFunc: fakeDevfileWithNameProvider("123456789"),
529
530 want: func(contextDir string, d *parser.DevfileObj) string { return "x123456789" },
531 },
532 {
533 name: "no name",
534 devfileProviderFunc: realDevfileWithNameProvider(""),
535 want: wantDevfileDirectoryName,
536 },
537 {
538 name: "blank name",
539 devfileProviderFunc: realDevfileWithNameProvider(" "),
540 want: wantDevfileDirectoryName,
541 },
542 {
543 name: "passing no devfile should use the context directory name",
544 devfileProviderFunc: func() (*parser.DevfileObj, string, error) {
545 dir, err := fs.TempDir("", "Component_GatherName_")
546 if err != nil {
547 return nil, dir, err
548 }
549 return nil, dir, nil
550 },
551 want: func(contextDir string, _ *parser.DevfileObj) string {
552 return util.GetDNS1123Name(filepath.Base(contextDir))
553 },
554 },
555 } {
556 t.Run(tt.name, func(t *testing.T) {
557 d, dir, dErr := tt.devfileProviderFunc()
558 if dir != "" {
559 defer func(fs filesystem.Filesystem, path string) {
560 if err := fs.RemoveAll(path); err != nil {
561 t.Logf("error while attempting to remove temporary directory %q: %v", path, err)
562 }
563 }(fs, dir)
564 }
565 if dErr != nil {
566 t.Errorf("error when building test Devfile object: %v", dErr)
567 return
568 }
569
570 got, err := GatherName(dir, d)
571 if (err != nil) != tt.wantErr {
572 t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
573 }
574 want := tt.want(dir, d)
575 if diff := cmp.Diff(want, got); diff != "" {
576 t.Errorf("GatherName() mismatch (-want +got):\n%s", diff)
577 }
578 })
579 }
580 }
581
582 func TestListRoutesAndIngresses(t *testing.T) {
583 const (
584 componentName = "nodejs-prj1-api-abhz"
585 k8sComponentName = "my-nodejs-app"
586 namespace = "my-namespace"
587 )
588 createFakeIngressFromDevfile := func(devfileObj parser.DevfileObj, ingressComponentName string, label map[string]string) *v1.Ingress {
589 ing := &v1.Ingress{}
590 uList, _ := libdevfile.GetK8sComponentAsUnstructuredList(devfileObj, ingressComponentName, "", filesystem.DefaultFs{})
591
592 u := uList[0]
593 _ = runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), ing)
594 ing.SetLabels(label)
595 return ing
596 }
597
598 createFakeRouteFromDevfile := func(devfileObj parser.DevfileObj, routeComponentName string, label map[string]string) *v12.Route {
599 route := &v12.Route{}
600 uList, _ := libdevfile.GetK8sComponentAsUnstructuredList(devfileObj, routeComponentName, "", filesystem.DefaultFs{})
601
602 u := uList[0]
603 _ = runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), route)
604 route.SetLabels(label)
605 return route
606 }
607
608 label := labels.GetLabels(componentName, "app", "", labels.ComponentDeployMode, false)
609
610 selector := labels.GetNameSelector(componentName)
611
612
613 devfileObjWithIngress := testingutil.GetTestDevfileObjFromFile("devfile-deploy-ingress.yaml")
614 ing := createFakeIngressFromDevfile(devfileObjWithIngress, "outerloop-url", label)
615 ingConnectionData := api.ConnectionData{
616 Name: k8sComponentName,
617 Rules: []api.Rules{
618 {
619 Host: "nodejs.example.com",
620 Paths: []string{"/", "/foo"}},
621 },
622 }
623
624
625 devfileObjWithDefaultBackendIngress := testingutil.GetTestDevfileObjFromFile("devfile-deploy-defaultBackend-ingress.yaml")
626 ingDefaultBackend := createFakeIngressFromDevfile(devfileObjWithDefaultBackendIngress, "outerloop-url", label)
627 ingDBConnectionData := api.ConnectionData{
628 Name: k8sComponentName,
629 Rules: []api.Rules{
630 {
631 Host: "*",
632 Paths: []string{"/*"}},
633 },
634 }
635
636
637 devfileObjWithRoute := testingutil.GetTestDevfileObjFromFile("devfile-deploy-route.yaml")
638 routeGVR := schema.GroupVersionResource{
639 Group: kclient.RouteGVK.Group,
640 Version: kclient.RouteGVK.Version,
641 Resource: "routes",
642 }
643 route := createFakeRouteFromDevfile(devfileObjWithRoute, "outerloop-url", label)
644 routeUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(route)
645 routeConnectionData := api.ConnectionData{
646 Name: k8sComponentName,
647 Rules: []api.Rules{
648 {
649 Host: "",
650 Paths: []string{"/foo"},
651 },
652 },
653 }
654
655 mockKubeClient := func(ctrl *gomock.Controller, isOCP bool, ingresses []v1.Ingress, routeUnstructured map[string]interface{}) kclient.ClientInterface {
656 client := kclient.NewMockClientInterface(ctrl)
657 client.EXPECT().GetCurrentNamespace().Return(namespace)
658 client.EXPECT().ListIngresses(namespace, selector).Return(&v1.IngressList{Items: ingresses}, nil)
659 client.EXPECT().IsProjectSupported().Return(isOCP, nil)
660 if isOCP {
661 client.EXPECT().GetGVRFromGVK(kclient.RouteGVK).Return(routeGVR, nil)
662 client.EXPECT().GetCurrentNamespace().Return(namespace)
663 client.EXPECT().ListDynamicResources(gomock.Any(), routeGVR, selector).Return(
664 &unstructured.UnstructuredList{Items: []unstructured.Unstructured{{Object: routeUnstructured}}}, nil)
665 }
666 return client
667 }
668 type args struct {
669 client func(ctrl *gomock.Controller) kclient.ClientInterface
670 componentName string
671 }
672 tests := []struct {
673 name string
674 args args
675 wantIngs []api.ConnectionData
676 wantRoutes []api.ConnectionData
677 wantErr bool
678 }{
679 {
680 name: "list both ingresses and routes",
681 args: args{
682 client: func(ctrl *gomock.Controller) kclient.ClientInterface {
683 return mockKubeClient(ctrl, true, []v1.Ingress{*ing}, routeUnstructured)
684 },
685 componentName: componentName,
686 },
687 wantIngs: []api.ConnectionData{ingConnectionData},
688 wantRoutes: []api.ConnectionData{routeConnectionData},
689 wantErr: false,
690 },
691 {
692 name: "list only ingresses when the cluster is not ocp",
693 args: args{
694 client: func(ctrl *gomock.Controller) kclient.ClientInterface {
695 return mockKubeClient(ctrl, false, []v1.Ingress{*ing}, nil)
696 },
697 componentName: componentName,
698 },
699 wantIngs: []api.ConnectionData{ingConnectionData},
700 wantRoutes: nil,
701 wantErr: false,
702 },
703 {
704 name: "list ingress with default backend and no rules",
705 args: args{
706 client: func(ctrl *gomock.Controller) kclient.ClientInterface {
707 return mockKubeClient(ctrl, false, []v1.Ingress{*ingDefaultBackend}, nil)
708 },
709 componentName: componentName,
710 },
711 wantIngs: []api.ConnectionData{ingDBConnectionData},
712 wantRoutes: nil,
713 wantErr: false,
714 },
715 {
716 name: "skip ingress if it has an owner reference",
717 args: args{
718 client: func(ctrl *gomock.Controller) kclient.ClientInterface {
719 ownedIng := ing
720 ownedIng.SetOwnerReferences([]metav1.OwnerReference{
721 {
722 APIVersion: route.APIVersion,
723 Kind: route.Kind,
724 Name: route.GetName(),
725 },
726 })
727 return mockKubeClient(ctrl, false, []v1.Ingress{*ownedIng}, nil)
728 },
729 componentName: componentName,
730 },
731 wantIngs: nil,
732 wantRoutes: nil,
733 wantErr: false,
734 },
735 {
736 name: "skip route if it has an owner reference",
737 args: args{client: func(ctrl *gomock.Controller) kclient.ClientInterface {
738 ownedRoute := route
739 ownedRoute.SetOwnerReferences([]metav1.OwnerReference{
740 {
741 APIVersion: "apps/v1",
742 Kind: "Deployment",
743 Name: "some-deployment",
744 },
745 })
746 ownedRouteUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(ownedRoute)
747 return mockKubeClient(ctrl, true, nil, ownedRouteUnstructured)
748 },
749 componentName: componentName,
750 },
751 wantIngs: nil,
752 wantRoutes: nil,
753 wantErr: false,
754 },
755 }
756 for _, tt := range tests {
757 t.Run(tt.name, func(t *testing.T) {
758 ctrl := gomock.NewController(t)
759 gotIngs, gotRoutes, err := ListRoutesAndIngresses(tt.args.client(ctrl), tt.args.componentName, "app")
760 if (err != nil) != tt.wantErr {
761 t.Errorf("ListRoutesAndIngresses() error = %v, wantErr %v", err, tt.wantErr)
762 return
763 }
764 if diff := cmp.Diff(tt.wantIngs, gotIngs); diff != "" {
765 t.Errorf("ListRoutesAndIngresses() wantIngs mismatch (-want +got):\n%s", diff)
766 }
767 if diff := cmp.Diff(tt.wantRoutes, gotRoutes); diff != "" {
768 t.Errorf("ListRoutesAndIngresses() wantRoutes mismatch (-want +got):\n%s", diff)
769 }
770 })
771 }
772 }
773
774 func TestGetDevfileInfo(t *testing.T) {
775 const kubeNs = "a-namespace"
776
777 packageManifestResource := unstructured.Unstructured{}
778 packageManifestResource.SetKind("PackageManifest")
779 packageManifestResource.SetLabels(labels.Builder().WithMode(labels.ComponentDevMode).Labels())
780
781 type args struct {
782 kubeClient func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface
783 podmanClient func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client
784 componentName string
785 }
786 tests := []struct {
787 name string
788 args args
789 want func() (parser.DevfileObj, error)
790 wantErr bool
791 }{
792 {
793 name: "no kube client and no podman client",
794 args: args{
795 componentName: "aname",
796 },
797 wantErr: true,
798 want: func() (parser.DevfileObj, error) {
799 return parser.DevfileObj{}, nil
800 },
801 },
802 {
803 name: "only kube client returning an error",
804 args: args{
805 componentName: "some-name",
806 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
807 c := kclient.NewMockClientInterface(ctrl)
808 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
809 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, errors.New("error"))
810 return c
811 },
812 },
813 wantErr: true,
814 want: func() (parser.DevfileObj, error) {
815 return parser.DevfileObj{}, nil
816 },
817 },
818 {
819 name: "only kube client returning an empty list",
820 args: args{
821 componentName: "some-name",
822 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
823 c := kclient.NewMockClientInterface(ctrl)
824 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
825 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, nil)
826 return c
827 },
828 },
829 wantErr: true,
830 want: func() (parser.DevfileObj, error) {
831 return parser.DevfileObj{}, nil
832 },
833 },
834 {
835 name: "only kube client returning PackageManifest resource",
836 args: args{
837 componentName: "some-name",
838 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
839 c := kclient.NewMockClientInterface(ctrl)
840 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
841 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(
842 []unstructured.Unstructured{packageManifestResource}, nil)
843 return c
844 },
845 },
846 wantErr: true,
847 want: func() (parser.DevfileObj, error) {
848 return parser.DevfileObj{}, nil
849 },
850 },
851 {
852 name: "only podman client returning an error",
853 args: args{
854 componentName: "some-name",
855 podmanClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client {
856 c := podman.NewMockClient(ctrl)
857 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, errors.New("error"))
858 return c
859 },
860 },
861 wantErr: true,
862 want: func() (parser.DevfileObj, error) {
863 return parser.DevfileObj{}, nil
864 },
865 },
866 {
867 name: "only podman client returning an empty list",
868 args: args{
869 componentName: "some-name",
870 podmanClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client {
871 c := podman.NewMockClient(ctrl)
872 c.EXPECT().GetAllResourcesFromSelector(gomock.Any(), gomock.Any()).Return(nil, nil)
873 return c
874 },
875 },
876 wantErr: true,
877 want: func() (parser.DevfileObj, error) {
878 return parser.DevfileObj{}, nil
879 },
880 },
881 {
882 name: "kube and podman clients returning same component with mismatching labels",
883 args: args{
884 componentName: "some-name",
885 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
886 u1 := unstructured.Unstructured{}
887 u1.SetLabels(labels.Builder().
888 WithComponentName(componentName).
889 WithMode(labels.ComponentDevMode).
890 WithProjectType("spring").
891 Labels())
892 u2 := unstructured.Unstructured{}
893 u2.SetLabels(labels.Builder().
894 WithComponentName(componentName).
895 WithMode(labels.ComponentDeployMode).
896 WithProjectType("spring").
897 Labels())
898 c := kclient.NewMockClientInterface(ctrl)
899 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
900 selector := labels.GetNameSelector(componentName)
901 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq(kubeNs)).
902 Return([]unstructured.Unstructured{u1, packageManifestResource, u2}, nil)
903 return c
904 },
905 podmanClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client {
906 u1 := unstructured.Unstructured{}
907 u1.SetLabels(labels.Builder().
908 WithComponentName(componentName).
909 WithMode(labels.ComponentDevMode).
910 WithProjectType("quarkus").
911 Labels())
912 c := podman.NewMockClient(ctrl)
913 selector := labels.GetNameSelector(componentName)
914 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq("")).
915 Return([]unstructured.Unstructured{u1}, nil)
916 return c
917 },
918 },
919 wantErr: true,
920 want: func() (parser.DevfileObj, error) {
921 return parser.DevfileObj{}, nil
922 },
923 },
924 {
925 name: "only kube client returning component",
926 args: args{
927 componentName: "some-name",
928 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
929 u1 := unstructured.Unstructured{}
930 u1.SetLabels(labels.Builder().
931 WithComponentName(componentName).
932 WithMode(labels.ComponentDeployMode).
933 WithProjectType("spring").
934 Labels())
935 c := kclient.NewMockClientInterface(ctrl)
936 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
937 selector := labels.GetNameSelector(componentName)
938 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq(kubeNs)).
939 Return([]unstructured.Unstructured{u1}, nil)
940 return c
941 },
942 podmanClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client {
943 c := podman.NewMockClient(ctrl)
944 selector := labels.GetNameSelector(componentName)
945 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq("")).Return(nil, nil)
946 return c
947 },
948 },
949 wantErr: false,
950 want: func() (parser.DevfileObj, error) {
951 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
952 if err != nil {
953 return parser.DevfileObj{}, err
954 }
955 metadata := devfileData.GetMetadata()
956 metadata.Name = "some-name"
957 metadata.DisplayName = UnknownValue
958 metadata.ProjectType = "spring"
959 metadata.Language = UnknownValue
960 metadata.Version = UnknownValue
961 metadata.Description = UnknownValue
962 devfileData.SetMetadata(metadata)
963 return parser.DevfileObj{
964 Data: devfileData,
965 }, nil
966 },
967 },
968 {
969 name: "only podman client returning component",
970 args: args{
971 componentName: "some-name",
972 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
973 c := kclient.NewMockClientInterface(ctrl)
974 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
975 selector := labels.GetNameSelector(componentName)
976 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq(kubeNs)).
977 Return(nil, nil)
978 return c
979 },
980 podmanClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client {
981 u1 := unstructured.Unstructured{}
982 u1.SetLabels(labels.Builder().
983 WithComponentName(componentName).
984 WithMode(labels.ComponentDevMode).
985 WithProjectType("quarkus").
986 Labels())
987 c := podman.NewMockClient(ctrl)
988 selector := labels.GetNameSelector(componentName)
989 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq("")).Return(
990 []unstructured.Unstructured{u1}, nil)
991 return c
992 },
993 },
994 wantErr: false,
995 want: func() (parser.DevfileObj, error) {
996 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
997 if err != nil {
998 return parser.DevfileObj{}, err
999 }
1000 metadata := devfileData.GetMetadata()
1001 metadata.Name = "some-name"
1002 metadata.DisplayName = UnknownValue
1003 metadata.ProjectType = "quarkus"
1004 metadata.Language = UnknownValue
1005 metadata.Version = UnknownValue
1006 metadata.Description = UnknownValue
1007 devfileData.SetMetadata(metadata)
1008 return parser.DevfileObj{
1009 Data: devfileData,
1010 }, nil
1011 },
1012 },
1013 {
1014 name: "both kube and podman clients returning component",
1015 args: args{
1016 componentName: "some-name",
1017 kubeClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) kclient.ClientInterface {
1018 u1 := unstructured.Unstructured{}
1019 u1.SetLabels(labels.Builder().
1020 WithComponentName(componentName).
1021 WithMode(labels.ComponentDeployMode).
1022 WithProjectType("nodejs").
1023 Labels())
1024 c := kclient.NewMockClientInterface(ctrl)
1025 c.EXPECT().GetCurrentNamespace().Return(kubeNs).AnyTimes()
1026 selector := labels.GetNameSelector(componentName)
1027 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq(kubeNs)).
1028 Return([]unstructured.Unstructured{u1}, nil)
1029 return c
1030 },
1031 podmanClient: func(ctx context.Context, ctrl *gomock.Controller, componentName string) podman.Client {
1032 u1 := unstructured.Unstructured{}
1033 u1.SetLabels(labels.Builder().
1034 WithComponentName(componentName).
1035 WithMode(labels.ComponentDevMode).
1036 WithProjectType("nodejs").
1037 Labels())
1038 c := podman.NewMockClient(ctrl)
1039 selector := labels.GetNameSelector(componentName)
1040 c.EXPECT().GetAllResourcesFromSelector(gomock.Eq(selector), gomock.Eq("")).Return(
1041 []unstructured.Unstructured{u1}, nil)
1042 return c
1043 },
1044 },
1045 wantErr: false,
1046 want: func() (parser.DevfileObj, error) {
1047 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
1048 if err != nil {
1049 return parser.DevfileObj{}, err
1050 }
1051 metadata := devfileData.GetMetadata()
1052 metadata.Name = "some-name"
1053 metadata.DisplayName = UnknownValue
1054 metadata.ProjectType = "nodejs"
1055 metadata.Language = UnknownValue
1056 metadata.Version = UnknownValue
1057 metadata.Description = UnknownValue
1058 devfileData.SetMetadata(metadata)
1059 return parser.DevfileObj{
1060 Data: devfileData,
1061 }, nil
1062 },
1063 },
1064 }
1065
1066 for _, tt := range tests {
1067 t.Run(tt.name, func(t *testing.T) {
1068 ctrl := gomock.NewController(t)
1069 ctx := odocontext.WithApplication(context.TODO(), "app")
1070 var kubeClient kclient.ClientInterface
1071 if tt.args.kubeClient != nil {
1072 kubeClient = tt.args.kubeClient(ctx, ctrl, tt.args.componentName)
1073 }
1074 var podmanClient podman.Client
1075 if tt.args.podmanClient != nil {
1076 podmanClient = tt.args.podmanClient(ctx, ctrl, tt.args.componentName)
1077 }
1078
1079 got, err := GetDevfileInfo(ctx, kubeClient, podmanClient, tt.args.componentName)
1080 if (err != nil) != tt.wantErr {
1081 t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
1082 }
1083 want, err := tt.want()
1084 if err != nil {
1085 t.Errorf("GetDevfileInfo() error while building wanted DevfileObj: %v", err)
1086 return
1087 }
1088 if diff := cmp.Diff(want, got, cmp.AllowUnexported(devfileCtx.DevfileCtx{})); diff != "" {
1089 t.Errorf("GetDevfileInfo() mismatch (-want +got):\n%s", diff)
1090 }
1091 })
1092 }
1093 }
1094
View as plain text