1 package libdevfile
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "testing"
8
9 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
10 devfilepkg "github.com/devfile/api/v2/pkg/devfile"
11 "github.com/devfile/library/v2/pkg/devfile/parser"
12 "github.com/devfile/library/v2/pkg/devfile/parser/data"
13 devfileFileSystem "github.com/devfile/library/v2/pkg/testingutil/filesystem"
14 dfutil "github.com/devfile/library/v2/pkg/util"
15 "github.com/golang/mock/gomock"
16 "github.com/google/go-cmp/cmp"
17 "k8s.io/utils/pointer"
18
19 "github.com/redhat-developer/odo/pkg/libdevfile/generator"
20 "github.com/redhat-developer/odo/pkg/testingutil"
21 "github.com/redhat-developer/odo/pkg/util"
22 )
23
24 var buildGroup = v1alpha2.BuildCommandGroupKind
25 var runGroup = v1alpha2.RunCommandGroupKind
26
27 func TestGetCommand(t *testing.T) {
28
29 commands := [...]string{"ls -la", "pwd"}
30 components := [...]string{"alias1", "alias2"}
31
32 tests := []struct {
33 name string
34 requestedType []v1alpha2.CommandGroupKind
35 execCommands []v1alpha2.Command
36 compCommands []v1alpha2.Command
37 reqCommandName string
38 retCommandName string
39 wantErr bool
40 wantPresent bool
41 }{
42 {
43 name: "Case 1: Valid devfile",
44 execCommands: []v1alpha2.Command{
45 getExecCommand("build", buildGroup),
46 getExecCommand("run", runGroup),
47 },
48 requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
49 wantErr: false,
50 wantPresent: true,
51 },
52 {
53 name: "Case 2: Valid devfile with devrun and devbuild",
54 execCommands: []v1alpha2.Command{
55 getExecCommand("build", buildGroup),
56 getExecCommand("run", runGroup),
57 },
58 requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
59 wantErr: false,
60 wantPresent: true,
61 },
62 {
63 name: "Case 3: Valid devfile with empty workdir",
64 execCommands: []v1alpha2.Command{
65 {
66 CommandUnion: v1alpha2.CommandUnion{
67 Exec: &v1alpha2.ExecCommand{
68 LabeledCommand: v1alpha2.LabeledCommand{
69 BaseCommand: v1alpha2.BaseCommand{
70 Group: &v1alpha2.CommandGroup{Kind: runGroup},
71 },
72 },
73 CommandLine: commands[0],
74 Component: components[0],
75 },
76 },
77 },
78 },
79 requestedType: []v1alpha2.CommandGroupKind{runGroup},
80 wantErr: false,
81 wantPresent: true,
82 },
83 {
84 name: "Case 4.1: Mismatched command type",
85 execCommands: []v1alpha2.Command{
86 {
87 Id: "build command",
88 CommandUnion: v1alpha2.CommandUnion{
89 Exec: &v1alpha2.ExecCommand{
90 LabeledCommand: v1alpha2.LabeledCommand{
91 BaseCommand: v1alpha2.BaseCommand{
92 Group: &v1alpha2.CommandGroup{Kind: runGroup},
93 },
94 },
95 CommandLine: commands[0],
96 Component: components[0],
97 },
98 },
99 },
100 },
101 reqCommandName: "build command",
102 requestedType: []v1alpha2.CommandGroupKind{buildGroup},
103 wantErr: true,
104 },
105 {
106 name: "Case 4.2: Matching command by name and type",
107 execCommands: []v1alpha2.Command{
108 {
109 Id: "build command",
110 CommandUnion: v1alpha2.CommandUnion{
111 Exec: &v1alpha2.ExecCommand{
112 LabeledCommand: v1alpha2.LabeledCommand{
113 BaseCommand: v1alpha2.BaseCommand{
114 Group: &v1alpha2.CommandGroup{Kind: buildGroup},
115 },
116 },
117 CommandLine: commands[0],
118 Component: components[0],
119 },
120 },
121 },
122 },
123 reqCommandName: "build command",
124 requestedType: []v1alpha2.CommandGroupKind{buildGroup},
125 wantErr: false,
126 wantPresent: true,
127 },
128 {
129 name: "Case 5: Default command is returned",
130 execCommands: []v1alpha2.Command{
131 {
132 Id: "defaultRunCommand",
133 CommandUnion: v1alpha2.CommandUnion{
134 Exec: &v1alpha2.ExecCommand{
135 LabeledCommand: v1alpha2.LabeledCommand{
136 BaseCommand: v1alpha2.BaseCommand{
137 Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
138 },
139 },
140 CommandLine: commands[0],
141 Component: components[0],
142 },
143 },
144 },
145 {
146 Id: "runCommand",
147 CommandUnion: v1alpha2.CommandUnion{
148 Exec: &v1alpha2.ExecCommand{
149 LabeledCommand: v1alpha2.LabeledCommand{
150 BaseCommand: v1alpha2.BaseCommand{
151 Group: &v1alpha2.CommandGroup{Kind: runGroup},
152 },
153 },
154 CommandLine: commands[0],
155 Component: components[0],
156 },
157 },
158 },
159 },
160 retCommandName: "defaultRunCommand",
161 requestedType: []v1alpha2.CommandGroupKind{runGroup},
162 wantErr: false,
163 wantPresent: true,
164 },
165 {
166 name: "Case 5.1: if only one command is present, it is returned and assumed as default",
167 execCommands: []v1alpha2.Command{
168 {
169 Id: "defaultRunCommand",
170 CommandUnion: v1alpha2.CommandUnion{
171 Exec: &v1alpha2.ExecCommand{
172 LabeledCommand: v1alpha2.LabeledCommand{
173 BaseCommand: v1alpha2.BaseCommand{
174 Group: &v1alpha2.CommandGroup{Kind: runGroup},
175 },
176 },
177 CommandLine: commands[0],
178 Component: components[0],
179 },
180 },
181 },
182 },
183 retCommandName: "defaultRunCommand",
184 requestedType: []v1alpha2.CommandGroupKind{runGroup},
185 wantErr: false,
186 wantPresent: true,
187 },
188 {
189 name: "Case 5.2: if multiple default commands are present, error is returned",
190 execCommands: []v1alpha2.Command{
191 {
192 Id: "runCommand1",
193 CommandUnion: v1alpha2.CommandUnion{
194 Exec: &v1alpha2.ExecCommand{
195 LabeledCommand: v1alpha2.LabeledCommand{
196 BaseCommand: v1alpha2.BaseCommand{
197 Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
198 },
199 },
200 CommandLine: commands[0],
201 Component: components[0],
202 },
203 },
204 }, {
205 Id: "runCommand2",
206 CommandUnion: v1alpha2.CommandUnion{
207 Exec: &v1alpha2.ExecCommand{
208 LabeledCommand: v1alpha2.LabeledCommand{
209 BaseCommand: v1alpha2.BaseCommand{
210 Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
211 },
212 },
213 CommandLine: commands[0],
214 Component: components[0],
215 },
216 },
217 },
218 },
219
220 requestedType: []v1alpha2.CommandGroupKind{runGroup},
221 wantErr: true,
222 },
223 {
224 name: "Case 5.2: if multiple default commands are present, error is returned",
225 execCommands: []v1alpha2.Command{
226 {
227 Id: "runCommand1",
228 CommandUnion: v1alpha2.CommandUnion{
229 Exec: &v1alpha2.ExecCommand{
230 LabeledCommand: v1alpha2.LabeledCommand{
231 BaseCommand: v1alpha2.BaseCommand{
232 Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
233 },
234 },
235 CommandLine: commands[0],
236 Component: components[0],
237 },
238 },
239 }, {
240 Id: "runCommand2",
241 CommandUnion: v1alpha2.CommandUnion{
242 Exec: &v1alpha2.ExecCommand{
243 LabeledCommand: v1alpha2.LabeledCommand{
244 BaseCommand: v1alpha2.BaseCommand{
245 Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
246 },
247 },
248 CommandLine: commands[0],
249 Component: components[0],
250 },
251 },
252 },
253 },
254
255 requestedType: []v1alpha2.CommandGroupKind{runGroup},
256 wantErr: true,
257 },
258 {
259 name: "Case 6: Composite command is returned",
260 execCommands: []v1alpha2.Command{
261 {
262 Id: "build",
263 CommandUnion: v1alpha2.CommandUnion{
264 Exec: &v1alpha2.ExecCommand{
265 LabeledCommand: v1alpha2.LabeledCommand{
266 BaseCommand: v1alpha2.BaseCommand{
267 Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(false)},
268 },
269 },
270 CommandLine: commands[0],
271 Component: components[0],
272 },
273 },
274 },
275 {
276 Id: "run",
277 CommandUnion: v1alpha2.CommandUnion{
278 Exec: &v1alpha2.ExecCommand{
279 LabeledCommand: v1alpha2.LabeledCommand{
280 BaseCommand: v1alpha2.BaseCommand{
281 Group: &v1alpha2.CommandGroup{Kind: runGroup},
282 },
283 },
284 CommandLine: commands[0],
285 Component: components[0],
286 },
287 },
288 },
289 },
290 compCommands: []v1alpha2.Command{
291 {
292 Id: "myComposite",
293 CommandUnion: v1alpha2.CommandUnion{
294 Composite: &v1alpha2.CompositeCommand{
295 LabeledCommand: v1alpha2.LabeledCommand{
296 BaseCommand: v1alpha2.BaseCommand{
297 Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(true)},
298 },
299 },
300 Commands: []string{"build", "run"},
301 },
302 },
303 },
304 },
305 retCommandName: "myComposite",
306 requestedType: []v1alpha2.CommandGroupKind{buildGroup},
307 wantErr: false,
308 wantPresent: true,
309 },
310 }
311 for _, tt := range tests {
312 t.Run(tt.name, func(t *testing.T) {
313 components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
314 devObj := parser.DevfileObj{
315 Data: func() data.DevfileData {
316 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
317 if err != nil {
318 t.Error(err)
319 }
320 err = devfileData.AddCommands(tt.execCommands)
321 if err != nil {
322 t.Error(err)
323 }
324 err = devfileData.AddCommands(tt.compCommands)
325 if err != nil {
326 t.Error(err)
327 }
328 err = devfileData.AddComponents(components)
329 if err != nil {
330 t.Error(err)
331 }
332 return devfileData
333 }(),
334 }
335
336 for _, gtype := range tt.requestedType {
337 cmd, ok, err := GetCommand(devObj, tt.reqCommandName, gtype)
338 if tt.wantErr != (err != nil) {
339 t.Errorf("TestGetCommand unexpected error for command: %v wantErr: %v err: %v", gtype, tt.wantErr, err)
340 return
341 } else if tt.wantErr {
342 return
343 }
344 if tt.wantPresent != ok {
345 t.Errorf("TestGetCommand unexpected presence for command: %v wantPresent: %v ok: %v", gtype, tt.wantPresent, ok)
346 return
347 }
348
349 if len(tt.retCommandName) > 0 && cmd.Id != tt.retCommandName {
350 t.Errorf("TestGetCommand error: command names do not match expected: %v actual: %v", tt.retCommandName, cmd.Id)
351 }
352 }
353 })
354 }
355
356 }
357
358 func TestDeploy(t *testing.T) {
359 deployDefault1 := generator.GetCompositeCommand(generator.CompositeCommandParams{
360 Kind: v1alpha2.DeployCommandGroupKind,
361 Id: "deploy-default-1",
362 IsDefault: pointer.Bool(true),
363 Commands: []string{"image-command", "deployment-command", "service-command"},
364 })
365 applyImageCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
366 Kind: v1alpha2.DeployCommandGroupKind,
367 Id: "image-command",
368 IsDefault: pointer.Bool(false),
369 Component: "image-component",
370 })
371 applyDeploymentCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
372 Kind: v1alpha2.DeployCommandGroupKind,
373 Id: "deployment-command",
374 IsDefault: pointer.Bool(false),
375 Component: "deployment-component",
376 })
377 applyServiceCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
378 Kind: v1alpha2.DeployCommandGroupKind,
379 Id: "service-command",
380 IsDefault: pointer.Bool(false),
381 Component: "service-component",
382 })
383
384 imageComponent := generator.GetImageComponent(generator.ImageComponentParams{
385 Name: "image-component",
386 Image: v1alpha2.Image{
387 ImageName: "an-image-name",
388 },
389 })
390 deploymentComponent := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
391 Name: "deployment-component",
392 Kubernetes: &v1alpha2.KubernetesComponent{},
393 })
394 serviceComponent := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
395 Name: "service-component",
396 Kubernetes: &v1alpha2.KubernetesComponent{},
397 })
398
399 type args struct {
400 devfileObj func() parser.DevfileObj
401 handler func(ctrl *gomock.Controller) Handler
402 }
403 tests := []struct {
404 name string
405 args args
406 wantErr bool
407 }{
408 {
409 name: "deploy an image and two kubernetes components",
410 args: args{
411 devfileObj: func() parser.DevfileObj {
412 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
413 _ = dData.AddCommands([]v1alpha2.Command{deployDefault1, applyImageCommand, applyDeploymentCommand, applyServiceCommand})
414 _ = dData.AddComponents([]v1alpha2.Component{imageComponent, deploymentComponent, serviceComponent})
415 return parser.DevfileObj{
416 Data: dData,
417 }
418 },
419 handler: func(ctrl *gomock.Controller) Handler {
420 h := NewMockHandler(ctrl)
421 h.EXPECT().ApplyImage(imageComponent)
422 h.EXPECT().ApplyKubernetes(deploymentComponent, v1alpha2.DeployCommandGroupKind)
423 h.EXPECT().ApplyKubernetes(serviceComponent, v1alpha2.DeployCommandGroupKind)
424 return h
425 },
426 },
427 },
428 {
429 name: "deploy with multiple deploy and no default",
430 args: args{
431 devfileObj: func() parser.DevfileObj {
432 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
433 _ = dData.AddCommands([]v1alpha2.Command{applyServiceCommand, applyDeploymentCommand})
434 _ = dData.AddComponents([]v1alpha2.Component{deploymentComponent, serviceComponent})
435 return parser.DevfileObj{
436 Data: dData,
437 }
438 },
439 handler: func(ctrl *gomock.Controller) Handler {
440 return NewMockHandler(ctrl)
441 },
442 },
443 wantErr: true,
444 },
445
446 }
447 for _, tt := range tests {
448 t.Run(tt.name, func(t *testing.T) {
449 ctrl := gomock.NewController(t)
450 if err := Deploy(context.Background(), tt.args.devfileObj(), tt.args.handler(ctrl)); (err != nil) != tt.wantErr {
451 t.Errorf("Deploy() error = %v, wantErr %v", err, tt.wantErr)
452 }
453 })
454 }
455 }
456
457 func TestBuild(t *testing.T) {
458 containerComp := v1alpha2.Component{
459 Name: "my-container",
460 ComponentUnion: v1alpha2.ComponentUnion{
461 Container: &v1alpha2.ContainerComponent{
462 Container: v1alpha2.Container{
463 Image: "my-image",
464 },
465 },
466 },
467 }
468 defaultBuildCommand := generator.GetExecCommand(generator.ExecCommandParams{
469 Kind: v1alpha2.BuildCommandGroupKind,
470 Id: "my-default-build-command",
471 IsDefault: pointer.Bool(true),
472 CommandLine: "build my-app",
473 Component: containerComp.Name,
474 })
475 nonDefaultBuildCommandExplicit := generator.GetExecCommand(generator.ExecCommandParams{
476 Kind: v1alpha2.BuildCommandGroupKind,
477 Id: "my-explicit-non-default-build-command",
478 IsDefault: pointer.Bool(false),
479 CommandLine: "build my-app",
480 Component: containerComp.Name,
481 })
482 nonDefaultBuildCommandImplicit := generator.GetExecCommand(generator.ExecCommandParams{
483 Kind: v1alpha2.BuildCommandGroupKind,
484 Id: "my-implicit-non-default-build-command",
485 CommandLine: "build my-app",
486 Component: containerComp.Name,
487 })
488
489 nonDefaultRunCommand := generator.GetExecCommand(generator.ExecCommandParams{
490 Kind: v1alpha2.RunCommandGroupKind,
491 Id: "my-non-default-run-command",
492 CommandLine: "run my-app",
493 Component: containerComp.Name,
494 })
495 defaultBuildCommandComposite := generator.GetCompositeCommand(generator.CompositeCommandParams{
496 Kind: v1alpha2.BuildCommandGroupKind,
497 Id: "my-default-build-command-composite",
498 IsDefault: pointer.Bool(true),
499 Commands: []string{"my-non-default-run-command"},
500 })
501 type args struct {
502 devfileObj func() parser.DevfileObj
503 handler func(ctrl *gomock.Controller) Handler
504 cmdName string
505 }
506 for _, tt := range []struct {
507 name string
508 args args
509 wantErr bool
510 }{
511 {
512 name: "missing default command",
513 args: args{
514 devfileObj: func() parser.DevfileObj {
515 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
516 _ = dData.AddCommands([]v1alpha2.Command{nonDefaultBuildCommandExplicit, nonDefaultBuildCommandImplicit})
517 _ = dData.AddComponents([]v1alpha2.Component{containerComp})
518 return parser.DevfileObj{
519 Data: dData,
520 }
521 },
522 handler: func(ctrl *gomock.Controller) Handler {
523 h := NewMockHandler(ctrl)
524 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(0)
525 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
526 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
527 return h
528 },
529 },
530 },
531 {
532 name: "with default command",
533 args: args{
534 devfileObj: func() parser.DevfileObj {
535 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
536 _ = dData.AddCommands([]v1alpha2.Command{defaultBuildCommand, nonDefaultBuildCommandExplicit, nonDefaultBuildCommandImplicit})
537 _ = dData.AddComponents([]v1alpha2.Component{containerComp})
538 return parser.DevfileObj{
539 Data: dData,
540 }
541 },
542 handler: func(ctrl *gomock.Controller) Handler {
543 h := NewMockHandler(ctrl)
544 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(1)
545 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
546 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
547 return h
548 },
549 },
550 },
551 {
552 name: "missing custom command",
553 args: args{
554 devfileObj: func() parser.DevfileObj {
555 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
556 _ = dData.AddCommands([]v1alpha2.Command{defaultBuildCommand})
557 _ = dData.AddComponents([]v1alpha2.Component{containerComp})
558 return parser.DevfileObj{
559 Data: dData,
560 }
561 },
562 handler: func(ctrl *gomock.Controller) Handler {
563 h := NewMockHandler(ctrl)
564 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(0)
565 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
566 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
567 return h
568 },
569 cmdName: "my-explicit-non-default-build-command",
570 },
571 wantErr: true,
572 },
573 {
574 name: "with custom command",
575 args: args{
576 devfileObj: func() parser.DevfileObj {
577 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
578 _ = dData.AddCommands([]v1alpha2.Command{nonDefaultBuildCommandExplicit, nonDefaultBuildCommandImplicit})
579 _ = dData.AddComponents([]v1alpha2.Component{containerComp})
580 return parser.DevfileObj{
581 Data: dData,
582 }
583 },
584 handler: func(ctrl *gomock.Controller) Handler {
585 h := NewMockHandler(ctrl)
586 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(0)
587 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(1)
588 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
589 return h
590 },
591 cmdName: "my-explicit-non-default-build-command",
592 },
593 },
594 {
595 name: "with default composite command",
596 args: args{
597 devfileObj: func() parser.DevfileObj {
598 dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
599 _ = dData.AddCommands([]v1alpha2.Command{defaultBuildCommandComposite, nonDefaultRunCommand})
600 _ = dData.AddComponents([]v1alpha2.Component{containerComp})
601 return parser.DevfileObj{
602 Data: dData,
603 }
604 },
605 handler: func(ctrl *gomock.Controller) Handler {
606 h := NewMockHandler(ctrl)
607 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultRunCommand)).Times(1)
608 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
609 h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
610 return h
611 },
612 },
613 },
614 } {
615 t.Run(tt.name, func(t *testing.T) {
616 err := Build(context.Background(), tt.args.devfileObj(), tt.args.cmdName, tt.args.handler(gomock.NewController(t)))
617 if (err != nil) != tt.wantErr {
618 t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
619 }
620 })
621 }
622 }
623
624 func TestGetContainerEndpointMapping(t *testing.T) {
625 type args struct {
626 containers []v1alpha2.Component
627 includeDebug bool
628 }
629
630 imageComponent := generator.GetImageComponent(generator.ImageComponentParams{
631 Name: "image-component",
632 Image: v1alpha2.Image{
633 ImageName: "an-image-name",
634 },
635 })
636
637 containerWithNoEndpoints := generator.GetContainerComponent(generator.ContainerComponentParams{
638 Name: "container 1",
639 Endpoints: nil,
640 })
641
642 containerWithOnePublicEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
643 Name: "container 2",
644 Endpoints: []v1alpha2.Endpoint{
645 {
646 Name: "ep1",
647 TargetPort: 8080,
648 Exposure: v1alpha2.PublicEndpointExposure,
649 },
650 },
651 })
652
653 containerWithOneInternalEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
654 Name: "container 3",
655 Endpoints: []v1alpha2.Endpoint{
656 {
657 Name: "ep2",
658 TargetPort: 9090,
659 Exposure: v1alpha2.InternalEndpointExposure,
660 },
661 },
662 })
663
664 containerWithOneNoneDebugEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
665 Name: "container-none-endpoint",
666 Endpoints: []v1alpha2.Endpoint{
667 {
668 Name: "debug",
669 TargetPort: 9099,
670 Exposure: v1alpha2.NoneEndpointExposure,
671 },
672 },
673 })
674 multiportContainer1 := generator.GetContainerComponent(generator.ContainerComponentParams{
675 Name: "container-multiple-endpoints-1",
676 Endpoints: []v1alpha2.Endpoint{
677 {
678 Name: "http-3000",
679 TargetPort: 3000,
680 Exposure: v1alpha2.PublicEndpointExposure,
681 },
682 {
683 Name: "http-8888",
684 TargetPort: 8888,
685 Exposure: v1alpha2.InternalEndpointExposure,
686 },
687 {
688 Name: "debug",
689 TargetPort: 5005,
690 Exposure: v1alpha2.NoneEndpointExposure,
691 },
692 {
693 Name: "debug-udp",
694 TargetPort: 15005,
695 Exposure: v1alpha2.NoneEndpointExposure,
696 Protocol: v1alpha2.UDPEndpointProtocol,
697 },
698 {
699 Name: "debug-ws",
700 TargetPort: 25005,
701 Exposure: v1alpha2.NoneEndpointExposure,
702 Protocol: v1alpha2.WSEndpointProtocol,
703 },
704 },
705 })
706 multiportContainer2 := generator.GetContainerComponent(generator.ContainerComponentParams{
707 Name: "container-multiple-endpoints-2",
708 Endpoints: []v1alpha2.Endpoint{
709 {
710 Name: "http-8080",
711 TargetPort: 8080,
712 Exposure: v1alpha2.PublicEndpointExposure,
713 },
714 {
715 Name: "http-9100",
716 TargetPort: 9100,
717 Exposure: v1alpha2.InternalEndpointExposure,
718 },
719 {
720 Name: "debug",
721 TargetPort: 18080,
722 Exposure: v1alpha2.NoneEndpointExposure,
723 },
724 {
725 Name: "debug-udp",
726 TargetPort: 19100,
727 Exposure: v1alpha2.NoneEndpointExposure,
728 Protocol: v1alpha2.UDPEndpointProtocol,
729 },
730 },
731 })
732
733 tests := []struct {
734 name string
735 args args
736 want map[string][]v1alpha2.Endpoint
737 }{
738 {
739 name: "invalid input - image components instead of container components",
740 args: args{
741 containers: []v1alpha2.Component{imageComponent},
742 },
743 want: map[string][]v1alpha2.Endpoint{},
744 },
745 {
746 name: "one container with no endpoints exposed",
747 args: args{
748 containers: []v1alpha2.Component{containerWithNoEndpoints},
749 },
750 want: map[string][]v1alpha2.Endpoint{},
751 },
752 {
753 name: "multiple containers with varying types of endpoints - without debug",
754 args: args{
755 containers: []v1alpha2.Component{
756 containerWithNoEndpoints,
757 containerWithOnePublicEndpoint,
758 containerWithOneInternalEndpoint,
759 containerWithOneNoneDebugEndpoint,
760 multiportContainer1,
761 multiportContainer2,
762 },
763 },
764 want: map[string][]v1alpha2.Endpoint{
765 containerWithOnePublicEndpoint.Name: {
766 v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
767 },
768 containerWithOneInternalEndpoint.Name: {
769 v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
770 },
771 multiportContainer1.Name: {
772 v1alpha2.Endpoint{Name: "http-3000", TargetPort: 3000, Exposure: "public"},
773 v1alpha2.Endpoint{Name: "http-8888", TargetPort: 8888, Exposure: "internal"},
774 },
775 multiportContainer2.Name: {
776 v1alpha2.Endpoint{Name: "http-8080", TargetPort: 8080, Exposure: "public"},
777 v1alpha2.Endpoint{Name: "http-9100", TargetPort: 9100, Exposure: "internal"},
778 },
779 },
780 },
781 {
782 name: "multiple containers with varying types of endpoints - with debug",
783 args: args{
784 containers: []v1alpha2.Component{
785 containerWithNoEndpoints,
786 containerWithOnePublicEndpoint,
787 containerWithOneInternalEndpoint,
788 containerWithOneNoneDebugEndpoint,
789 multiportContainer1,
790 multiportContainer2,
791 },
792 includeDebug: true,
793 },
794 want: map[string][]v1alpha2.Endpoint{
795 containerWithOnePublicEndpoint.Name: {
796 v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
797 },
798 containerWithOneInternalEndpoint.Name: {
799 v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
800 },
801 containerWithOneNoneDebugEndpoint.Name: {
802 v1alpha2.Endpoint{Name: "debug", TargetPort: 9099, Exposure: "none"},
803 },
804 multiportContainer1.Name: {
805 v1alpha2.Endpoint{Name: "http-3000", TargetPort: 3000, Exposure: "public"},
806 v1alpha2.Endpoint{Name: "http-8888", TargetPort: 8888, Exposure: "internal"},
807 v1alpha2.Endpoint{Name: "debug", TargetPort: 5005, Exposure: "none"},
808 v1alpha2.Endpoint{Name: "debug-udp", TargetPort: 15005, Exposure: "none", Protocol: "udp"},
809 v1alpha2.Endpoint{Name: "debug-ws", TargetPort: 25005, Exposure: "none", Protocol: "ws"},
810 },
811 multiportContainer2.Name: {
812 v1alpha2.Endpoint{Name: "http-8080", TargetPort: 8080, Exposure: "public"},
813 v1alpha2.Endpoint{Name: "http-9100", TargetPort: 9100, Exposure: "internal"},
814 v1alpha2.Endpoint{Name: "debug", TargetPort: 18080, Exposure: "none"},
815 v1alpha2.Endpoint{Name: "debug-udp", TargetPort: 19100, Exposure: "none", Protocol: "udp"},
816 },
817 },
818 },
819 {
820 name: "invalid input - one image component with rest being containers - without debug",
821 args: args{
822 containers: []v1alpha2.Component{
823 containerWithNoEndpoints,
824 containerWithOnePublicEndpoint,
825 containerWithOneInternalEndpoint,
826 containerWithOneNoneDebugEndpoint,
827 imageComponent,
828 },
829 },
830 want: map[string][]v1alpha2.Endpoint{
831 containerWithOnePublicEndpoint.Name: {
832 v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
833 },
834 containerWithOneInternalEndpoint.Name: {
835 v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
836 },
837 },
838 },
839 {
840 name: "invalid input - one image component with rest being containers - with debug",
841 args: args{
842 containers: []v1alpha2.Component{
843 containerWithNoEndpoints,
844 containerWithOnePublicEndpoint,
845 containerWithOneInternalEndpoint,
846 containerWithOneNoneDebugEndpoint,
847 imageComponent,
848 },
849 includeDebug: true,
850 },
851 want: map[string][]v1alpha2.Endpoint{
852 containerWithOnePublicEndpoint.Name: {
853 v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
854 },
855 containerWithOneInternalEndpoint.Name: {
856 v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
857 },
858 containerWithOneNoneDebugEndpoint.Name: {
859 v1alpha2.Endpoint{Name: "debug", TargetPort: 9099, Exposure: "none"},
860 },
861 },
862 },
863 }
864 for _, tt := range tests {
865 t.Run(tt.name, func(t *testing.T) {
866 got := GetContainerEndpointMapping(tt.args.containers, tt.args.includeDebug)
867
868 if diff := cmp.Diff(tt.want, got); diff != "" {
869 t.Errorf("GetContainerEndpointMapping() mismatch (-want +got):\n%s", diff)
870 }
871 })
872 }
873 }
874
875 func TestGetEndpointsFromDevfile(t *testing.T) {
876 type args struct {
877 devfileObj func() parser.DevfileObj
878 ignoreExposures []v1alpha2.EndpointExposure
879 }
880 ep1 := v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: v1alpha2.NoneEndpointExposure}
881 ep2 := v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: v1alpha2.InternalEndpointExposure}
882 ep3 := v1alpha2.Endpoint{Name: "ep3", TargetPort: 8888, Exposure: v1alpha2.PublicEndpointExposure}
883
884 container := generator.GetContainerComponent(generator.ContainerComponentParams{
885 Name: "container-1",
886 Endpoints: []v1alpha2.Endpoint{ep1, ep2, ep3},
887 })
888 tests := []struct {
889 name string
890 args args
891 want []v1alpha2.Endpoint
892 wantErr bool
893 }{
894 {
895 name: "Ignore exposure of type none",
896 args: args{
897 devfileObj: func() parser.DevfileObj {
898 data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
899 _ = data.AddComponents([]v1alpha2.Component{container})
900 return parser.DevfileObj{
901 Data: data,
902 }
903 },
904 ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.NoneEndpointExposure},
905 },
906 want: []v1alpha2.Endpoint{ep2, ep3},
907 },
908 {
909 name: "Ignore exposure of type public",
910 args: args{
911 devfileObj: func() parser.DevfileObj {
912 data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
913 _ = data.AddComponents([]v1alpha2.Component{container})
914 return parser.DevfileObj{
915 Data: data,
916 }
917 },
918 ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.PublicEndpointExposure},
919 },
920 want: []v1alpha2.Endpoint{ep1, ep2},
921 },
922 {
923 name: "Ignore exposure of type internal",
924 args: args{
925 devfileObj: func() parser.DevfileObj {
926 data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
927 _ = data.AddComponents([]v1alpha2.Component{container})
928 return parser.DevfileObj{
929 Data: data,
930 }
931 },
932 ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.InternalEndpointExposure},
933 },
934 want: []v1alpha2.Endpoint{ep1, ep3},
935 },
936 {
937 name: "Ignore none",
938 args: args{
939 devfileObj: func() parser.DevfileObj {
940 data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
941 _ = data.AddComponents([]v1alpha2.Component{container})
942 return parser.DevfileObj{
943 Data: data,
944 }
945 },
946 ignoreExposures: []v1alpha2.EndpointExposure{},
947 },
948 want: []v1alpha2.Endpoint{ep1, ep2, ep3},
949 },
950 {
951 name: "Ignore all exposure types",
952 args: args{
953 devfileObj: func() parser.DevfileObj {
954 data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
955 _ = data.AddComponents([]v1alpha2.Component{container})
956 return parser.DevfileObj{
957 Data: data,
958 }
959 },
960 ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.InternalEndpointExposure, v1alpha2.NoneEndpointExposure, v1alpha2.PublicEndpointExposure},
961 },
962 want: nil,
963 },
964 }
965 for _, tt := range tests {
966 t.Run(tt.name, func(t *testing.T) {
967 got, err := GetEndpointsFromDevfile(tt.args.devfileObj(), tt.args.ignoreExposures)
968 if (err != nil) != tt.wantErr {
969 t.Errorf("GetEndpointsFromDevfile() error = %v, wantErr %v", err, tt.wantErr)
970 return
971 }
972 if diff := cmp.Diff(tt.want, got); diff != "" {
973 t.Errorf("GetEndpointsFromDevfile() mismatch (-want +got):\n%s", diff)
974 }
975 })
976 }
977 }
978
979 func TestIsDebugEndpoint(t *testing.T) {
980 type args struct {
981 endpoint v1alpha2.Endpoint
982 }
983 for _, tt := range []struct {
984 name string
985 args args
986 want bool
987 }{
988 {
989 name: "exactly debug",
990 args: args{endpoint: v1alpha2.Endpoint{Name: "debug", TargetPort: 5005}},
991 want: true,
992 },
993 {
994 name: "exactly debug - case-sensitive",
995 args: args{endpoint: v1alpha2.Endpoint{Name: "DEBUG", TargetPort: 5005}},
996 },
997 {
998 name: "starting with debug",
999 args: args{endpoint: v1alpha2.Endpoint{Name: "debug-port", TargetPort: 5005}},
1000 want: true,
1001 },
1002 {
1003 name: "starting with debug - case sensitive",
1004 args: args{endpoint: v1alpha2.Endpoint{Name: "DEBUG-PORT", TargetPort: 5005}},
1005 },
1006 {
1007 name: "containing debug",
1008 args: args{endpoint: v1alpha2.Endpoint{Name: "my-debug", TargetPort: 5005}},
1009 },
1010 {
1011 name: "containing debug prefix",
1012 args: args{endpoint: v1alpha2.Endpoint{Name: "my-debug-port", TargetPort: 5005}},
1013 },
1014 {
1015 name: "any other string",
1016 args: args{endpoint: v1alpha2.Endpoint{Name: "lorem-ipsum", TargetPort: 5005}},
1017 },
1018 } {
1019 t.Run(tt.name, func(t *testing.T) {
1020 got := IsDebugEndpoint(tt.args.endpoint)
1021 if diff := cmp.Diff(tt.want, got); diff != "" {
1022 t.Errorf("IsDebugEndpoint() mismatch (-want +got):\n%s", diff)
1023 }
1024 })
1025 }
1026 }
1027
1028 func TestGetDebugEndpointsForComponent(t *testing.T) {
1029 type args struct {
1030 cmpProvider func() v1alpha2.Component
1031 }
1032
1033 for _, tt := range []struct {
1034 name string
1035 args args
1036 wantErr bool
1037 want []v1alpha2.Endpoint
1038 }{
1039 {
1040 name: "not a container component",
1041 args: args{
1042 cmpProvider: func() v1alpha2.Component { return testingutil.GetFakeVolumeComponent("vol-comp", "1Gi") },
1043 },
1044 wantErr: true,
1045 },
1046 {
1047 name: "no endpoints in container component",
1048 args: args{
1049 cmpProvider: func() v1alpha2.Component { return testingutil.GetFakeContainerComponent("my-container-comp") },
1050 },
1051 },
1052 {
1053 name: "mix of debug and non-debug endpoints in container component",
1054 args: args{
1055 cmpProvider: func() v1alpha2.Component {
1056 comp := testingutil.GetFakeContainerComponent("my-container-comp", 8080, 3000, 9090)
1057 comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
1058 Name: "debug",
1059 TargetPort: 5005,
1060 Exposure: v1alpha2.NoneEndpointExposure,
1061 })
1062 comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
1063 Name: "debug1",
1064 TargetPort: 15005,
1065 Exposure: v1alpha2.InternalEndpointExposure,
1066 })
1067 comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
1068 Name: "debug-port2",
1069 TargetPort: 5006,
1070 Exposure: v1alpha2.InternalEndpointExposure,
1071 })
1072 comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
1073 Name: "debug-port3-public",
1074 TargetPort: 5007,
1075 Exposure: v1alpha2.PublicEndpointExposure,
1076 })
1077 return comp
1078 },
1079 },
1080 want: []v1alpha2.Endpoint{
1081 {
1082 Name: "debug",
1083 TargetPort: 5005,
1084 Exposure: v1alpha2.NoneEndpointExposure,
1085 },
1086 {
1087 Name: "debug-port2",
1088 TargetPort: 5006,
1089 Exposure: v1alpha2.InternalEndpointExposure,
1090 },
1091 {
1092 Name: "debug-port3-public",
1093 TargetPort: 5007,
1094 Exposure: v1alpha2.PublicEndpointExposure,
1095 },
1096 },
1097 },
1098 } {
1099 t.Run(tt.name, func(t *testing.T) {
1100 got, err := GetDebugEndpointsForComponent(tt.args.cmpProvider())
1101
1102 if tt.wantErr != (err != nil) {
1103 t.Errorf("GetDebugEndpointsForComponent(), wantErr: %v, err: %v", tt.wantErr, err)
1104 }
1105 if diff := cmp.Diff(tt.want, got); diff != "" {
1106 t.Errorf("GetDebugEndpointsForComponent() mismatch (-want +got):\n%s", diff)
1107 }
1108 })
1109 }
1110 }
1111
1112 func TestGetK8sManifestsWithVariablesSubstituted(t *testing.T) {
1113 fakeFs := devfileFileSystem.NewFakeFs()
1114 cmpName := "my-cmp-1"
1115 for _, tt := range []struct {
1116 name string
1117 setupFunc func() error
1118 devfileObjFunc func() parser.DevfileObj
1119 wantErr bool
1120 want string
1121 }{
1122 {
1123 name: "Missing Component",
1124 devfileObjFunc: func() parser.DevfileObj {
1125 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1126 cmp := generator.GetContainerComponent(generator.ContainerComponentParams{
1127 Name: "a-different-component",
1128 })
1129 s := v1alpha2.DevWorkspaceTemplateSpec{
1130 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1131 Components: []v1alpha2.Component{cmp},
1132 },
1133 }
1134 devfileData.SetDevfileWorkspaceSpec(s)
1135 return parser.DevfileObj{
1136 Data: devfileData,
1137 }
1138 },
1139 wantErr: true,
1140 },
1141 {
1142 name: "Multiple Components with the same name",
1143 devfileObjFunc: func() parser.DevfileObj {
1144 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1145 cmp1 := generator.GetContainerComponent(generator.ContainerComponentParams{
1146 Name: cmpName,
1147 })
1148 cmp2 := generator.GetImageComponent(generator.ImageComponentParams{
1149 Name: cmpName,
1150 })
1151 s := v1alpha2.DevWorkspaceTemplateSpec{
1152 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1153 Components: []v1alpha2.Component{cmp1, cmp2},
1154 },
1155 }
1156 devfileData.SetDevfileWorkspaceSpec(s)
1157 return parser.DevfileObj{
1158 Data: devfileData,
1159 }
1160 },
1161 wantErr: true,
1162 },
1163 {
1164 name: "Container Component",
1165 devfileObjFunc: func() parser.DevfileObj {
1166 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1167 cmp := generator.GetContainerComponent(generator.ContainerComponentParams{
1168 Name: cmpName,
1169 })
1170 s := v1alpha2.DevWorkspaceTemplateSpec{
1171 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1172 Components: []v1alpha2.Component{cmp},
1173 },
1174 }
1175 devfileData.SetDevfileWorkspaceSpec(s)
1176 return parser.DevfileObj{
1177 Data: devfileData,
1178 }
1179 },
1180 wantErr: true,
1181 },
1182 {
1183 name: "Image Component",
1184 devfileObjFunc: func() parser.DevfileObj {
1185 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1186 cmp := generator.GetImageComponent(generator.ImageComponentParams{
1187 Name: cmpName,
1188 })
1189 s := v1alpha2.DevWorkspaceTemplateSpec{
1190 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1191 Components: []v1alpha2.Component{cmp},
1192 },
1193 }
1194 devfileData.SetDevfileWorkspaceSpec(s)
1195 return parser.DevfileObj{
1196 Data: devfileData,
1197 }
1198 },
1199 wantErr: true,
1200 },
1201 {
1202 name: "Kubernetes Component - Inlined with no variables",
1203 devfileObjFunc: func() parser.DevfileObj {
1204 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1205 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1206 Name: cmpName,
1207 Kubernetes: &v1alpha2.KubernetesComponent{
1208 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1209 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1210 Inlined: "some-text-inlined",
1211 },
1212 },
1213 },
1214 })
1215 s := v1alpha2.DevWorkspaceTemplateSpec{
1216 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1217 Components: []v1alpha2.Component{cmp},
1218 },
1219 }
1220 devfileData.SetDevfileWorkspaceSpec(s)
1221 return parser.DevfileObj{
1222 Data: devfileData,
1223 }
1224 },
1225 wantErr: false,
1226 want: "some-text-inlined",
1227 },
1228 {
1229 name: "Kubernetes Component - Inlined with variables",
1230 devfileObjFunc: func() parser.DevfileObj {
1231 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1232 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1233 Name: cmpName,
1234 Kubernetes: &v1alpha2.KubernetesComponent{
1235 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1236 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1237 Inlined: "image: {{MY_CONTAINER_IMAGE}}",
1238 },
1239 },
1240 },
1241 })
1242 s := v1alpha2.DevWorkspaceTemplateSpec{
1243 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1244 Variables: map[string]string{
1245 "MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image:1.2.3",
1246 },
1247 Components: []v1alpha2.Component{cmp},
1248 },
1249 }
1250 devfileData.SetDevfileWorkspaceSpec(s)
1251 return parser.DevfileObj{
1252 Data: devfileData,
1253 }
1254 },
1255 wantErr: false,
1256 want: "image: quay.io/unknown-account/my-image:1.2.3",
1257 },
1258 {
1259 name: "Kubernetes Component - Inlined with unknown variables",
1260 devfileObjFunc: func() parser.DevfileObj {
1261 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1262 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1263 Name: cmpName,
1264 Kubernetes: &v1alpha2.KubernetesComponent{
1265 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1266 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1267 Inlined: "image: {{MY_CONTAINER_IMAGE}}:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
1268 },
1269 },
1270 },
1271 })
1272 s := v1alpha2.DevWorkspaceTemplateSpec{
1273 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1274 Variables: map[string]string{
1275 "MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
1276 },
1277 Components: []v1alpha2.Component{cmp},
1278 },
1279 }
1280 devfileData.SetDevfileWorkspaceSpec(s)
1281 return parser.DevfileObj{
1282 Data: devfileData,
1283 }
1284 },
1285 wantErr: true,
1286 want: "image: quay.io/unknown-account/my-image:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
1287 },
1288 {
1289 name: "Kubernetes Component - non-existing external file",
1290 devfileObjFunc: func() parser.DevfileObj {
1291 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1292 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1293 Name: cmpName,
1294 Kubernetes: &v1alpha2.KubernetesComponent{
1295 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1296 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1297 Uri: "kubernetes/my-external-file-with-should-not-exist",
1298 },
1299 },
1300 },
1301 })
1302 s := v1alpha2.DevWorkspaceTemplateSpec{
1303 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1304 Components: []v1alpha2.Component{cmp},
1305 },
1306 }
1307 devfileData.SetDevfileWorkspaceSpec(s)
1308 return parser.DevfileObj{
1309 Data: devfileData,
1310 }
1311 },
1312 wantErr: true,
1313 },
1314 {
1315 name: "Kubernetes Component - URI with no variables",
1316 setupFunc: func() error {
1317 return fakeFs.WriteFile("kubernetes/my-external-file",
1318 []byte("some-text-with-no-variables"),
1319 os.ModePerm)
1320 },
1321 devfileObjFunc: func() parser.DevfileObj {
1322 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1323 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1324 Name: cmpName,
1325 Kubernetes: &v1alpha2.KubernetesComponent{
1326 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1327 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1328 Uri: "kubernetes/my-external-file",
1329 },
1330 },
1331 },
1332 })
1333 s := v1alpha2.DevWorkspaceTemplateSpec{
1334 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1335 Components: []v1alpha2.Component{cmp},
1336 },
1337 }
1338 devfileData.SetDevfileWorkspaceSpec(s)
1339 return parser.DevfileObj{
1340 Data: devfileData,
1341 }
1342 },
1343 wantErr: false,
1344 want: "some-text-with-no-variables",
1345 },
1346 {
1347 name: "Kubernetes Component - URI with variables",
1348 setupFunc: func() error {
1349 return fakeFs.WriteFile("kubernetes/my-deployment.yaml",
1350 []byte("image: {{ MY_CONTAINER_IMAGE }}:{{MY_CONTAINER_IMAGE_VERSION}}"),
1351 os.ModePerm)
1352 },
1353 devfileObjFunc: func() parser.DevfileObj {
1354 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1355 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1356 Name: cmpName,
1357 Kubernetes: &v1alpha2.KubernetesComponent{
1358 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1359 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1360 Uri: "kubernetes/my-deployment.yaml",
1361 },
1362 },
1363 },
1364 })
1365 s := v1alpha2.DevWorkspaceTemplateSpec{
1366 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1367 Variables: map[string]string{
1368 "MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
1369 "MY_CONTAINER_IMAGE_VERSION": "1.2.3",
1370 },
1371 Components: []v1alpha2.Component{cmp},
1372 },
1373 }
1374 devfileData.SetDevfileWorkspaceSpec(s)
1375 return parser.DevfileObj{
1376 Data: devfileData,
1377 }
1378 },
1379 wantErr: false,
1380 want: "image: quay.io/unknown-account/my-image:1.2.3",
1381 },
1382 {
1383 name: "Kubernetes Component - URI with unknown variables",
1384 setupFunc: func() error {
1385 return fakeFs.WriteFile("kubernetes/my-external-file.yaml",
1386 []byte("image: {{MY_CONTAINER_IMAGE}}:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}"),
1387 os.ModePerm)
1388 },
1389 devfileObjFunc: func() parser.DevfileObj {
1390 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1391 cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
1392 Name: cmpName,
1393 Kubernetes: &v1alpha2.KubernetesComponent{
1394 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1395 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1396 Uri: "kubernetes/my-external-file.yaml",
1397 },
1398 },
1399 },
1400 })
1401 s := v1alpha2.DevWorkspaceTemplateSpec{
1402 DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
1403 Variables: map[string]string{
1404 "MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
1405 },
1406 Components: []v1alpha2.Component{cmp},
1407 },
1408 }
1409 devfileData.SetDevfileWorkspaceSpec(s)
1410 return parser.DevfileObj{
1411 Data: devfileData,
1412 }
1413 },
1414 wantErr: true,
1415 want: "image: quay.io/unknown-account/my-image:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
1416 },
1417 } {
1418 t.Run(tt.name, func(t *testing.T) {
1419 if tt.setupFunc != nil {
1420 if err := tt.setupFunc(); err != nil {
1421 t.Errorf("setup function returned an error: %v", err)
1422 return
1423 }
1424 }
1425 if tt.devfileObjFunc == nil {
1426 t.Error("devfileObjFunc function not defined for test case")
1427 return
1428 }
1429
1430 got, err := GetK8sManifestsWithVariablesSubstituted(tt.devfileObjFunc(), cmpName, "", fakeFs)
1431 if (err != nil) != tt.wantErr {
1432 t.Errorf("GetK8sManifestsWithVariablesSubstituted() error = %v, wantErr %v",
1433 err, tt.wantErr)
1434 return
1435 }
1436 if got != tt.want {
1437 t.Errorf("GetK8sManifestsWithVariablesSubstituted() got = %v, want %v",
1438 got, tt.want)
1439 }
1440 })
1441 }
1442 }
1443
1444 func TestValidateAndGetCommand(t *testing.T) {
1445
1446 commands := [...]string{"ls -la", "pwd"}
1447 components := [...]string{"alias1", "alias2"}
1448
1449 tests := []struct {
1450 name string
1451 requestedType []v1alpha2.CommandGroupKind
1452 execCommands []v1alpha2.Command
1453 compCommands []v1alpha2.Command
1454 reqCommandName string
1455 retCommandName []string
1456 wantErr bool
1457 }{
1458 {
1459 name: "Case 1: Valid devfile, default command returned even if it is not marked as IsDefault",
1460 execCommands: []v1alpha2.Command{
1461 getExecCommand("build", buildGroup),
1462 getExecCommand("run", runGroup),
1463 },
1464 requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
1465 wantErr: false,
1466 retCommandName: []string{"build", "run"},
1467 },
1468 {
1469 name: "Case 2: Valid devfile, but error returned because multiple build commands without default",
1470 execCommands: []v1alpha2.Command{
1471 getExecCommand("build", buildGroup),
1472 getExecCommand("build2", buildGroup),
1473 getExecCommand("run", runGroup),
1474 },
1475 requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
1476 wantErr: true,
1477 },
1478 {
1479 name: "Case 3: Valid devfile with empty workdir",
1480 execCommands: []v1alpha2.Command{
1481 {
1482 Id: "run",
1483 CommandUnion: v1alpha2.CommandUnion{
1484 Exec: &v1alpha2.ExecCommand{
1485 LabeledCommand: v1alpha2.LabeledCommand{
1486 BaseCommand: v1alpha2.BaseCommand{
1487 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1488 },
1489 },
1490 CommandLine: commands[0],
1491 Component: components[0],
1492 },
1493 },
1494 },
1495 },
1496 requestedType: []v1alpha2.CommandGroupKind{runGroup},
1497 wantErr: false,
1498 retCommandName: []string{"run"},
1499 },
1500 {
1501 name: "Case 4.1: Mismatched command type",
1502 execCommands: []v1alpha2.Command{
1503 {
1504 Id: "build command",
1505 CommandUnion: v1alpha2.CommandUnion{
1506 Exec: &v1alpha2.ExecCommand{
1507 LabeledCommand: v1alpha2.LabeledCommand{
1508 BaseCommand: v1alpha2.BaseCommand{
1509 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1510 },
1511 },
1512 CommandLine: commands[0],
1513 Component: components[0],
1514 },
1515 },
1516 },
1517 },
1518 reqCommandName: "build command",
1519 requestedType: []v1alpha2.CommandGroupKind{buildGroup},
1520 wantErr: true,
1521 },
1522 {
1523 name: "Case 4.2: Matching command by name and type",
1524 execCommands: []v1alpha2.Command{
1525 {
1526 Id: "build command",
1527 CommandUnion: v1alpha2.CommandUnion{
1528 Exec: &v1alpha2.ExecCommand{
1529 LabeledCommand: v1alpha2.LabeledCommand{
1530 BaseCommand: v1alpha2.BaseCommand{
1531 Group: &v1alpha2.CommandGroup{Kind: buildGroup},
1532 },
1533 },
1534 CommandLine: commands[0],
1535 Component: components[0],
1536 },
1537 },
1538 },
1539 },
1540 reqCommandName: "build command",
1541 requestedType: []v1alpha2.CommandGroupKind{buildGroup},
1542 retCommandName: []string{"build command"},
1543 wantErr: false,
1544 },
1545 {
1546 name: "Case 5: Default command is returned",
1547 execCommands: []v1alpha2.Command{
1548 {
1549 Id: "defaultRunCommand",
1550 CommandUnion: v1alpha2.CommandUnion{
1551 Exec: &v1alpha2.ExecCommand{
1552 LabeledCommand: v1alpha2.LabeledCommand{
1553 BaseCommand: v1alpha2.BaseCommand{
1554 Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
1555 },
1556 },
1557 CommandLine: commands[0],
1558 Component: components[0],
1559 },
1560 },
1561 },
1562 {
1563 Id: "runCommand",
1564 CommandUnion: v1alpha2.CommandUnion{
1565 Exec: &v1alpha2.ExecCommand{
1566 LabeledCommand: v1alpha2.LabeledCommand{
1567 BaseCommand: v1alpha2.BaseCommand{
1568 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1569 },
1570 },
1571 CommandLine: commands[0],
1572 Component: components[0],
1573 },
1574 },
1575 },
1576 },
1577 retCommandName: []string{"defaultRunCommand"},
1578 requestedType: []v1alpha2.CommandGroupKind{runGroup},
1579 wantErr: false,
1580 },
1581 {
1582 name: "Case 6: Composite command is returned",
1583 execCommands: []v1alpha2.Command{
1584 {
1585 Id: "build",
1586 CommandUnion: v1alpha2.CommandUnion{
1587 Exec: &v1alpha2.ExecCommand{
1588 LabeledCommand: v1alpha2.LabeledCommand{
1589 BaseCommand: v1alpha2.BaseCommand{
1590 Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(false)},
1591 },
1592 },
1593 CommandLine: commands[0],
1594 Component: components[0],
1595 },
1596 },
1597 },
1598 {
1599 Id: "run",
1600 CommandUnion: v1alpha2.CommandUnion{
1601 Exec: &v1alpha2.ExecCommand{
1602 LabeledCommand: v1alpha2.LabeledCommand{
1603 BaseCommand: v1alpha2.BaseCommand{
1604 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1605 },
1606 },
1607 CommandLine: commands[0],
1608 Component: components[0],
1609 },
1610 },
1611 },
1612 },
1613 compCommands: []v1alpha2.Command{
1614 {
1615 Id: "myComposite",
1616 CommandUnion: v1alpha2.CommandUnion{
1617 Composite: &v1alpha2.CompositeCommand{
1618 LabeledCommand: v1alpha2.LabeledCommand{
1619 BaseCommand: v1alpha2.BaseCommand{
1620 Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(true)},
1621 },
1622 },
1623 Commands: []string{"build", "run"},
1624 },
1625 },
1626 },
1627 },
1628 retCommandName: []string{"myComposite"},
1629 requestedType: []v1alpha2.CommandGroupKind{buildGroup},
1630 wantErr: false,
1631 },
1632 }
1633 for _, tt := range tests {
1634 t.Run(tt.name, func(t *testing.T) {
1635
1636 if !tt.wantErr && (len(tt.requestedType) != len(tt.retCommandName)) {
1637 t.Errorf("Invalid test definition %q requestedType length must match retCommandName length.", tt.name)
1638 }
1639 components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
1640 devObj := parser.DevfileObj{
1641 Data: func() data.DevfileData {
1642 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
1643 if err != nil {
1644 t.Error(err)
1645 }
1646 err = devfileData.AddCommands(tt.execCommands)
1647 if err != nil {
1648 t.Error(err)
1649 }
1650 err = devfileData.AddCommands(tt.compCommands)
1651 if err != nil {
1652 t.Error(err)
1653 }
1654 err = devfileData.AddComponents(components)
1655 if err != nil {
1656 t.Error(err)
1657 }
1658 return devfileData
1659 }(),
1660 }
1661
1662 for i, gtype := range tt.requestedType {
1663 cmd, err := ValidateAndGetCommand(devObj, tt.reqCommandName, gtype)
1664 if tt.wantErr != (err != nil) {
1665 t.Errorf("TestGetCommand unexpected error for command: %v wantErr: %v err: %v", gtype, tt.wantErr, err)
1666 return
1667 } else if tt.wantErr {
1668 return
1669 }
1670
1671 if cmd.Id != tt.retCommandName[i] {
1672 t.Errorf("TestGetCommand error: command names do not match expected: %v actual: %v", tt.retCommandName[i], cmd.Id)
1673 }
1674 }
1675 })
1676 }
1677
1678 }
1679
1680 func TestValidateAndGetPushCommands(t *testing.T) {
1681
1682 command := "ls -la"
1683 component := "alias1"
1684 workDir := "/"
1685 emptyString := ""
1686
1687 execCommands := []v1alpha2.Command{
1688 {
1689 Id: "run command",
1690 CommandUnion: v1alpha2.CommandUnion{
1691 Exec: &v1alpha2.ExecCommand{
1692 LabeledCommand: v1alpha2.LabeledCommand{
1693 BaseCommand: v1alpha2.BaseCommand{
1694 Group: &v1alpha2.CommandGroup{
1695 Kind: runGroup,
1696 IsDefault: util.GetBool(true),
1697 },
1698 },
1699 },
1700 CommandLine: command,
1701 Component: component,
1702 WorkingDir: workDir,
1703 },
1704 },
1705 },
1706
1707 {
1708 Id: "build command",
1709 CommandUnion: v1alpha2.CommandUnion{
1710 Exec: &v1alpha2.ExecCommand{
1711 LabeledCommand: v1alpha2.LabeledCommand{
1712 BaseCommand: v1alpha2.BaseCommand{
1713 Group: &v1alpha2.CommandGroup{Kind: buildGroup},
1714 },
1715 },
1716 CommandLine: command,
1717 Component: component,
1718 WorkingDir: workDir,
1719 },
1720 },
1721 },
1722
1723 {
1724 Id: "customcommand",
1725 CommandUnion: v1alpha2.CommandUnion{
1726 Exec: &v1alpha2.ExecCommand{
1727 LabeledCommand: v1alpha2.LabeledCommand{
1728 BaseCommand: v1alpha2.BaseCommand{
1729 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1730 },
1731 },
1732 CommandLine: command,
1733 Component: component,
1734 WorkingDir: workDir,
1735 },
1736 },
1737 },
1738 }
1739
1740 defaultBuildCommand := v1alpha2.Command{
1741 Id: "default build command",
1742 CommandUnion: v1alpha2.CommandUnion{
1743 Exec: &v1alpha2.ExecCommand{
1744 LabeledCommand: v1alpha2.LabeledCommand{
1745 BaseCommand: v1alpha2.BaseCommand{
1746 Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(true)},
1747 },
1748 },
1749 CommandLine: command,
1750 Component: component,
1751 WorkingDir: workDir,
1752 },
1753 },
1754 }
1755
1756 wrongCompTypeCmd := v1alpha2.Command{
1757
1758 Id: "wrong",
1759 CommandUnion: v1alpha2.CommandUnion{
1760 Exec: &v1alpha2.ExecCommand{
1761 LabeledCommand: v1alpha2.LabeledCommand{
1762 BaseCommand: v1alpha2.BaseCommand{
1763 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1764 },
1765 },
1766 CommandLine: command,
1767 Component: "",
1768 WorkingDir: workDir,
1769 },
1770 },
1771 }
1772
1773 tests := []struct {
1774 name string
1775 buildCommand string
1776 runCommand string
1777 execCommands []v1alpha2.Command
1778 numberOfCommands int
1779 missingBuildCommand bool
1780 wantErr bool
1781 }{
1782 {
1783 name: "Case 1: Default Devfile Commands",
1784 buildCommand: emptyString,
1785 runCommand: emptyString,
1786 execCommands: execCommands,
1787 numberOfCommands: 2,
1788 wantErr: false,
1789 },
1790 {
1791 name: "Case 2: Default Build Command, and Provided Run Command",
1792 buildCommand: emptyString,
1793 runCommand: "customcommand",
1794 execCommands: execCommands,
1795
1796 numberOfCommands: 2,
1797 wantErr: false,
1798 },
1799 {
1800 name: "Case 2.2: Default Build Command, and Default Run Command",
1801 buildCommand: emptyString,
1802 runCommand: emptyString,
1803 execCommands: append(execCommands, defaultBuildCommand),
1804 numberOfCommands: 2,
1805 wantErr: false,
1806 },
1807 {
1808 name: "Case 3: Empty Component",
1809 buildCommand: "customcommand",
1810 runCommand: "customcommand",
1811 execCommands: append(execCommands, wrongCompTypeCmd),
1812 numberOfCommands: 0,
1813 wantErr: true,
1814 },
1815 {
1816 name: "Case 4: Provided Wrong Build Command and Provided Run Command",
1817 buildCommand: "customcommand123",
1818 runCommand: "customcommand",
1819 execCommands: execCommands,
1820 numberOfCommands: 1,
1821 wantErr: true,
1822 },
1823 {
1824 name: "Case 5: Missing Build Command, and Provided Run Command",
1825 buildCommand: emptyString,
1826 runCommand: "customcommand",
1827 execCommands: []v1alpha2.Command{
1828 {
1829 Id: "customcommand",
1830 CommandUnion: v1alpha2.CommandUnion{
1831 Exec: &v1alpha2.ExecCommand{
1832 LabeledCommand: v1alpha2.LabeledCommand{
1833 BaseCommand: v1alpha2.BaseCommand{
1834 Group: &v1alpha2.CommandGroup{Kind: runGroup},
1835 },
1836 },
1837 Component: component,
1838 CommandLine: command,
1839 },
1840 },
1841 },
1842 },
1843 numberOfCommands: 1,
1844 wantErr: false,
1845 },
1846 }
1847
1848 for _, tt := range tests {
1849 t.Run(tt.name, func(t *testing.T) {
1850 devObj := parser.DevfileObj{
1851 Data: func() data.DevfileData {
1852 devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
1853 if err != nil {
1854 t.Error(err)
1855 }
1856 err = devfileData.AddCommands(tt.execCommands)
1857 if err != nil {
1858 t.Error(err)
1859 }
1860 err = devfileData.AddComponents(([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)}))
1861 if err != nil {
1862 t.Error(err)
1863 }
1864 return devfileData
1865 }(),
1866 }
1867
1868 pushCommands, err := ValidateAndGetPushCommands(devObj, tt.buildCommand, tt.runCommand)
1869 if !tt.wantErr == (err != nil) {
1870 t.Errorf("TestValidateAndGetPushDevfileCommands unexpected error when validating commands wantErr: %v err: %v", tt.wantErr, err)
1871 } else if tt.wantErr && err != nil {
1872 return
1873 }
1874
1875 if len(pushCommands) != tt.numberOfCommands {
1876 t.Errorf("TestValidateAndGetPushDevfileCommands error: wrong number of validated commands expected: %v actual :%v", tt.numberOfCommands, len(pushCommands))
1877 }
1878 })
1879 }
1880
1881 }
1882
1883 func TestGetContainerComponentsForCommand(t *testing.T) {
1884 devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
1885 devfileData.SetMetadata(devfilepkg.DevfileMetadata{Name: "my-app"})
1886 _ = devfileData.AddComponents([]v1alpha2.Component{
1887 {
1888 Name: "my-container1",
1889 ComponentUnion: v1alpha2.ComponentUnion{
1890 Container: &v1alpha2.ContainerComponent{
1891 Container: v1alpha2.Container{Image: "my-image"},
1892 },
1893 },
1894 },
1895 {
1896 Name: "my-container2",
1897 ComponentUnion: v1alpha2.ComponentUnion{
1898 Container: &v1alpha2.ContainerComponent{
1899 Container: v1alpha2.Container{Image: "my-image"},
1900 },
1901 },
1902 },
1903 {
1904 Name: "my-container3",
1905 ComponentUnion: v1alpha2.ComponentUnion{
1906 Container: &v1alpha2.ContainerComponent{
1907 Container: v1alpha2.Container{Image: "my-image"},
1908 },
1909 },
1910 },
1911 {
1912 Name: "my-image1",
1913 ComponentUnion: v1alpha2.ComponentUnion{
1914 Image: &v1alpha2.ImageComponent{
1915 Image: v1alpha2.Image{
1916 ImageName: "my-image",
1917 },
1918 },
1919 },
1920 },
1921 {
1922 Name: "my-k8s",
1923 ComponentUnion: v1alpha2.ComponentUnion{
1924 Kubernetes: &v1alpha2.KubernetesComponent{
1925 K8sLikeComponent: v1alpha2.K8sLikeComponent{
1926 K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
1927 Inlined: "---",
1928 },
1929 },
1930 },
1931 },
1932 },
1933 })
1934
1935 customCmd := v1alpha2.Command{
1936 Id: "custom",
1937 CommandUnion: v1alpha2.CommandUnion{
1938 Custom: &v1alpha2.CustomCommand{},
1939 },
1940 }
1941 execCmdCont1 := v1alpha2.Command{
1942 Id: "execCmdCont1",
1943 CommandUnion: v1alpha2.CommandUnion{
1944 Exec: &v1alpha2.ExecCommand{Component: "my-container1"},
1945 },
1946 }
1947 execCmdCont2 := v1alpha2.Command{
1948 Id: "execCmdCont2",
1949 CommandUnion: v1alpha2.CommandUnion{
1950 Exec: &v1alpha2.ExecCommand{Component: "my-container2"},
1951 },
1952 }
1953 execCmdCont3 := v1alpha2.Command{
1954 Id: "execCmdCont3",
1955 CommandUnion: v1alpha2.CommandUnion{
1956 Exec: &v1alpha2.ExecCommand{Component: "my-container3"},
1957 },
1958 }
1959 applyCmdCont1 := v1alpha2.Command{
1960 Id: "applyCmdCont1",
1961 CommandUnion: v1alpha2.CommandUnion{
1962 Apply: &v1alpha2.ApplyCommand{Component: "my-container1"},
1963 },
1964 }
1965 applyCmdImg1 := v1alpha2.Command{
1966 Id: "applyCmdImg1",
1967 CommandUnion: v1alpha2.CommandUnion{
1968 Apply: &v1alpha2.ApplyCommand{Component: "my-image1"},
1969 },
1970 }
1971 applyK8s1 := v1alpha2.Command{
1972 Id: "applyK8s1",
1973 CommandUnion: v1alpha2.CommandUnion{
1974 Apply: &v1alpha2.ApplyCommand{Component: "my-k8s"},
1975 },
1976 }
1977 childCompositeCmd := v1alpha2.Command{
1978 Id: "child-composite",
1979 CommandUnion: v1alpha2.CommandUnion{
1980 Composite: &v1alpha2.CompositeCommand{
1981 Commands: []string{execCmdCont3.Id, applyK8s1.Id},
1982 },
1983 },
1984 }
1985
1986 _ = devfileData.AddCommands([]v1alpha2.Command{
1987 customCmd, execCmdCont1, execCmdCont2, execCmdCont3, applyCmdCont1, applyCmdImg1, applyK8s1, childCompositeCmd})
1988
1989 devfileObj := parser.DevfileObj{Data: devfileData}
1990
1991 type args struct {
1992 cmd v1alpha2.Command
1993 }
1994 for _, tt := range []struct {
1995 name string
1996 args args
1997 wantErr bool
1998 want []string
1999 }{
2000 {
2001 name: "zero -value command",
2002 args: args{cmd: v1alpha2.Command{}},
2003 },
2004 {
2005 name: "GetCommandType returning an error",
2006 args: args{cmd: v1alpha2.Command{Id: "unknown"}},
2007 wantErr: true,
2008 },
2009 {
2010 name: "non-supported command type",
2011 args: args{cmd: customCmd},
2012 wantErr: true,
2013 },
2014 {
2015 name: "exec command matching existing container component",
2016 args: args{cmd: execCmdCont1},
2017 want: []string{"my-container1"},
2018 },
2019 {
2020 name: "exec command not matching existing container component",
2021 args: args{
2022 cmd: v1alpha2.Command{
2023 CommandUnion: v1alpha2.CommandUnion{
2024 Exec: &v1alpha2.ExecCommand{Component: "my-k8s"},
2025 },
2026 },
2027 },
2028 },
2029 {
2030 name: "apply command matching existing container component",
2031 args: args{cmd: applyCmdCont1},
2032 want: []string{"my-container1"},
2033 },
2034 {
2035 name: "apply command not matching existing container component",
2036 args: args{cmd: applyCmdImg1},
2037 },
2038 {
2039 name: "composite command with one command missing not declared in Devfile commands",
2040 args: args{
2041 cmd: v1alpha2.Command{
2042 CommandUnion: v1alpha2.CommandUnion{
2043 Composite: &v1alpha2.CompositeCommand{
2044 Commands: []string{execCmdCont1.Id, "a-command-not-found-in-devfile"},
2045 },
2046 },
2047 },
2048 },
2049 wantErr: true,
2050 },
2051 {
2052 name: "composite command with at least one unsupported component",
2053 args: args{
2054 cmd: v1alpha2.Command{
2055 CommandUnion: v1alpha2.CommandUnion{
2056 Composite: &v1alpha2.CompositeCommand{
2057 Commands: []string{childCompositeCmd.Id, customCmd.Id, applyCmdImg1.Id},
2058 },
2059 },
2060 },
2061 },
2062 wantErr: true,
2063 },
2064 {
2065 name: "composite command with no non-unsupported component",
2066 args: args{
2067 cmd: v1alpha2.Command{
2068 CommandUnion: v1alpha2.CommandUnion{
2069 Composite: &v1alpha2.CompositeCommand{
2070 Commands: []string{childCompositeCmd.Id, applyCmdImg1.Id, execCmdCont1.Id},
2071 },
2072 },
2073 },
2074 },
2075 want: []string{"my-container3", "my-container1"},
2076 },
2077 } {
2078 t.Run(tt.name, func(t *testing.T) {
2079 got, err := GetContainerComponentsForCommand(devfileObj, tt.args.cmd)
2080
2081 if tt.wantErr != (err != nil) {
2082 t.Errorf("unexpected error, wantErr: %v, err: %v", tt.wantErr, err)
2083 }
2084 if diff := cmp.Diff(tt.want, got); diff != "" {
2085 t.Errorf("GetContainerComponentsForCommand() mismatch (-want +got):\n%s", diff)
2086 }
2087 })
2088 }
2089 }
2090
2091 func getExecCommand(id string, group v1alpha2.CommandGroupKind) v1alpha2.Command {
2092 if len(id) == 0 {
2093 id = fmt.Sprintf("%s-%s", "cmd", dfutil.GenerateRandomString(10))
2094 }
2095 commands := [...]string{"ls -la", "pwd"}
2096 components := [...]string{"alias1", "alias2"}
2097 workDir := [...]string{"/", "/root"}
2098
2099 return v1alpha2.Command{
2100 Id: id,
2101 CommandUnion: v1alpha2.CommandUnion{
2102 Exec: &v1alpha2.ExecCommand{
2103 LabeledCommand: v1alpha2.LabeledCommand{
2104 BaseCommand: v1alpha2.BaseCommand{
2105 Group: &v1alpha2.CommandGroup{Kind: group},
2106 },
2107 },
2108 CommandLine: commands[0],
2109 Component: components[0],
2110 WorkingDir: workDir[0],
2111 },
2112 },
2113 }
2114
2115 }
2116
View as plain text