...

Source file src/github.com/redhat-developer/odo/pkg/dev/kubedev/utils/utils_test.go

Documentation: github.com/redhat-developer/odo/pkg/dev/kubedev/utils

     1  package utils
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/devfile/library/v2/pkg/devfile/parser/data"
     7  	"github.com/google/go-cmp/cmp"
     8  
     9  	"github.com/redhat-developer/odo/pkg/storage"
    10  	"github.com/redhat-developer/odo/pkg/util"
    11  
    12  	devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
    13  	devfileParser "github.com/devfile/library/v2/pkg/devfile/parser"
    14  	corev1 "k8s.io/api/core/v1"
    15  )
    16  
    17  func TestAddOdoProjectVolume(t *testing.T) {
    18  
    19  	tests := []struct {
    20  		name                         string
    21  		containers                   []corev1.Container
    22  		containerWithProjectVolMount []string
    23  		volMount                     map[string]string
    24  	}{
    25  		{
    26  			name:       "Case: nil passed as containers slice",
    27  			containers: nil,
    28  		},
    29  		{
    30  			name: "Case: Various containers with and without PROJECTS_ROOT",
    31  			containers: []corev1.Container{
    32  				{
    33  					Name: "container1",
    34  					Env: []corev1.EnvVar{
    35  						{
    36  							Name:  _envProjectsRoot,
    37  							Value: "/path1",
    38  						},
    39  					},
    40  				},
    41  				{
    42  					Name: "container2",
    43  					Env: []corev1.EnvVar{
    44  						{
    45  							Name:  _envProjectsRoot,
    46  							Value: "/path2",
    47  						},
    48  					},
    49  				},
    50  				{
    51  					Name: "container3",
    52  					Env: []corev1.EnvVar{
    53  						{
    54  							Name:  "RANDOM",
    55  							Value: "/path3",
    56  						},
    57  					},
    58  				},
    59  			},
    60  			containerWithProjectVolMount: []string{"container1", "container2"},
    61  			volMount: map[string]string{
    62  				"container1": "/path1",
    63  				"container2": "/path2",
    64  			},
    65  		},
    66  	}
    67  	for _, tt := range tests {
    68  		t.Run(tt.name, func(t *testing.T) {
    69  
    70  			AddOdoProjectVolume(tt.containers)
    71  
    72  			for wantContainerName, wantMountPath := range tt.volMount {
    73  				matched := false
    74  				for _, container := range tt.containers {
    75  					if container.Name == wantContainerName {
    76  						for _, volMount := range container.VolumeMounts {
    77  							if volMount.Name == storage.OdoSourceVolume && volMount.MountPath == wantMountPath {
    78  								matched = true
    79  							}
    80  						}
    81  					}
    82  				}
    83  
    84  				if !matched {
    85  					t.Error("TestAddOdoProjectVolume error: did not match the volume mount for odo-projects")
    86  				}
    87  			}
    88  		})
    89  	}
    90  }
    91  
    92  func TestAddOdoMandatoryVolume(t *testing.T) {
    93  	findContainerByName := func(containers []corev1.Container, name string) (corev1.Container, bool) {
    94  		for _, c := range containers {
    95  			if c.Name == name {
    96  				return c, true
    97  			}
    98  		}
    99  		return corev1.Container{}, false
   100  	}
   101  
   102  	hasVolumeMount := func(volumeMounts []corev1.VolumeMount, mountPath string, volName string) bool {
   103  		for _, v := range volumeMounts {
   104  			if v.Name == volName && v.MountPath == mountPath {
   105  				return true
   106  			}
   107  		}
   108  		return false
   109  	}
   110  
   111  	for _, tt := range []struct {
   112  		name             string
   113  		containers       []corev1.Container
   114  		wantVolumeMounts map[string]map[string]string
   115  	}{
   116  		{
   117  			name:       "nil as containers slice",
   118  			containers: nil,
   119  		},
   120  		{
   121  			name: "containers with no existing volume mounts",
   122  			containers: []corev1.Container{
   123  				{
   124  					Name: "container1",
   125  				},
   126  				{
   127  					Name: "container2",
   128  				},
   129  			},
   130  			wantVolumeMounts: map[string]map[string]string{
   131  				"container1": {storage.SharedDataMountPath: storage.SharedDataVolumeName},
   132  				"container2": {storage.SharedDataMountPath: storage.SharedDataVolumeName},
   133  			},
   134  		},
   135  		{
   136  			name: "containers with existing volume mounts",
   137  			containers: []corev1.Container{
   138  				{
   139  					Name: "container1",
   140  					VolumeMounts: []corev1.VolumeMount{
   141  						{
   142  							Name:      "vol1",
   143  							MountPath: "/container1/vol1",
   144  						},
   145  						{
   146  							Name:      "vol2",
   147  							MountPath: "/container1/vol2",
   148  						},
   149  					},
   150  				},
   151  				{
   152  					Name: "container2",
   153  					VolumeMounts: []corev1.VolumeMount{
   154  						{
   155  							Name:      "vol2",
   156  							MountPath: "/container2/vol2",
   157  						},
   158  					},
   159  				},
   160  				{
   161  					Name: "container3",
   162  				},
   163  			},
   164  			wantVolumeMounts: map[string]map[string]string{
   165  				"container1": {
   166  					"/container1/vol1":          "vol1",
   167  					"/container1/vol2":          "vol2",
   168  					storage.SharedDataMountPath: storage.SharedDataVolumeName,
   169  				},
   170  				"container2": {
   171  					"/container2/vol2":          "vol2",
   172  					storage.SharedDataMountPath: storage.SharedDataVolumeName,
   173  				},
   174  				"container3": {storage.SharedDataMountPath: storage.SharedDataVolumeName},
   175  			},
   176  		},
   177  	} {
   178  		t.Run(tt.name, func(t *testing.T) {
   179  			AddOdoMandatoryVolume(tt.containers)
   180  
   181  			for containerName, volMounts := range tt.wantVolumeMounts {
   182  				c, ok := findContainerByName(tt.containers, containerName)
   183  				if !ok {
   184  					t.Errorf("container %s defined in expected volume mounts, but not in container list for test",
   185  						containerName)
   186  				}
   187  				for mountPath, vol := range volMounts {
   188  					if !hasVolumeMount(c.VolumeMounts, mountPath, vol) {
   189  						t.Errorf("expected %s to be mounted under %s in container %s", vol, mountPath, c.Name)
   190  					}
   191  				}
   192  			}
   193  		})
   194  	}
   195  }
   196  
   197  func TestUpdateContainersEntrypointsIfNeeded(t *testing.T) {
   198  	const (
   199  		buildCommand            = "my-build"
   200  		buildCmdLine            = "echo my-build-command-line"
   201  		buildContainerComponent = "build-container-component"
   202  	)
   203  	const (
   204  		runCommand            = "my-run"
   205  		runCmdLine            = "echo my-run-command-line"
   206  		runContainerComponent = "run-container-component"
   207  	)
   208  	const (
   209  		debugCommand            = "my-debug"
   210  		debugCmdLine            = "echo my-run-command-line"
   211  		debugContainerComponent = "debug-container-component"
   212  	)
   213  
   214  	execBuildGroup := devfilev1.CommandGroup{
   215  		IsDefault: util.GetBool(true),
   216  		Kind:      devfilev1.BuildCommandGroupKind,
   217  	}
   218  	execRunGroup := devfilev1.CommandGroup{
   219  		IsDefault: util.GetBool(true),
   220  		Kind:      devfilev1.RunCommandGroupKind,
   221  	}
   222  	execDebugGroup := devfilev1.CommandGroup{
   223  		IsDefault: util.GetBool(true),
   224  		Kind:      devfilev1.DebugCommandGroupKind,
   225  	}
   226  
   227  	for _, tt := range []struct {
   228  		name                  string
   229  		commands              []devfilev1.Command
   230  		components            []devfilev1.Component
   231  		buildCommand          string
   232  		runCommand            string
   233  		debugCommand          string
   234  		buildContainerCommand []string
   235  		runContainerCommand   []string
   236  		debugContainerCommand []string
   237  		buildContainerArgs    []string
   238  		runContainerArgs      []string
   239  		debugContainerArgs    []string
   240  		wantErr               bool
   241  		// key is the container name
   242  		expectedContainerCommand map[string][]string
   243  		// key is the container name
   244  		expectedContainerArgs map[string][]string
   245  	}{
   246  		{
   247  			name:       "invalid run command",
   248  			runCommand: "a-non-existing-run-name",
   249  			wantErr:    true,
   250  		},
   251  		{
   252  			name:         "invalid debug command",
   253  			runCommand:   runCommand,
   254  			debugCommand: "a-non-existing-debug-name",
   255  			wantErr:      true,
   256  		},
   257  		{
   258  			name:         "missing build command specified by name",
   259  			buildCommand: buildCommand + "-not-found",
   260  			runCommand:   runCommand,
   261  			debugCommand: debugCommand,
   262  			wantErr:      true,
   263  		},
   264  		{
   265  			name:         "containers without any command or args => must be overridden with 'tail -f /dev/null'",
   266  			buildCommand: buildCommand,
   267  			runCommand:   runCommand,
   268  			debugCommand: debugCommand,
   269  			wantErr:      false,
   270  			expectedContainerCommand: map[string][]string{
   271  				buildContainerComponent: {"tail"},
   272  				runContainerComponent:   {"tail"},
   273  				debugContainerComponent: {"tail"},
   274  			},
   275  			expectedContainerArgs: map[string][]string{
   276  				buildContainerComponent: {"-f", "/dev/null"},
   277  				runContainerComponent:   {"-f", "/dev/null"},
   278  				debugContainerComponent: {"-f", "/dev/null"},
   279  			},
   280  		},
   281  		{
   282  			name:                  "containers with one without any command or args => must be overridden with 'tail -f /dev/null'",
   283  			buildCommand:          buildCommand,
   284  			runCommand:            runCommand,
   285  			debugCommand:          debugCommand,
   286  			wantErr:               false,
   287  			buildContainerCommand: []string{"npm"},
   288  			buildContainerArgs:    []string{"install"},
   289  			runContainerCommand:   []string{"printenv"},
   290  			runContainerArgs:      []string{"HOSTNAME"},
   291  			expectedContainerCommand: map[string][]string{
   292  				buildContainerComponent: {"npm"},
   293  				runContainerComponent:   {"printenv"},
   294  				debugContainerComponent: {"tail"},
   295  			},
   296  			expectedContainerArgs: map[string][]string{
   297  				buildContainerComponent: {"install"},
   298  				runContainerComponent:   {"HOSTNAME"},
   299  				debugContainerComponent: {"-f", "/dev/null"},
   300  			},
   301  		},
   302  		{
   303  			name:                "default build command, containers with one without any command or args => must be overridden with 'tail -f /dev/null'",
   304  			runCommand:          runCommand,
   305  			debugCommand:        debugCommand,
   306  			wantErr:             false,
   307  			runContainerCommand: []string{"printenv"},
   308  			runContainerArgs:    []string{"HOSTNAME"},
   309  			expectedContainerCommand: map[string][]string{
   310  				buildContainerComponent: {"tail"},
   311  				runContainerComponent:   {"printenv"},
   312  				debugContainerComponent: {"tail"},
   313  			},
   314  			expectedContainerArgs: map[string][]string{
   315  				buildContainerComponent: {"-f", "/dev/null"},
   316  				runContainerComponent:   {"HOSTNAME"},
   317  				debugContainerComponent: {"-f", "/dev/null"},
   318  			},
   319  		},
   320  		{
   321  			name:                  "containers with explicit command or args",
   322  			buildCommand:          buildCommand,
   323  			runCommand:            runCommand,
   324  			debugCommand:          debugCommand,
   325  			wantErr:               false,
   326  			buildContainerCommand: []string{"npm"},
   327  			buildContainerArgs:    []string{"install"},
   328  			runContainerCommand:   []string{"printenv"},
   329  			runContainerArgs:      []string{"HOSTNAME"},
   330  			debugContainerCommand: []string{"tail"},
   331  			debugContainerArgs:    []string{"-f", "/path/to/my/custom/log/file"},
   332  			expectedContainerCommand: map[string][]string{
   333  				buildContainerComponent: {"npm"},
   334  				runContainerComponent:   {"printenv"},
   335  				debugContainerComponent: {"tail"},
   336  			},
   337  			expectedContainerArgs: map[string][]string{
   338  				buildContainerComponent: {"install"},
   339  				runContainerComponent:   {"HOSTNAME"},
   340  				debugContainerComponent: {"-f", "/path/to/my/custom/log/file"},
   341  			},
   342  		},
   343  	} {
   344  		t.Run(tt.name, func(t *testing.T) {
   345  			devfileData, err := data.NewDevfileData(string(data.APISchemaVersion220))
   346  			if err != nil {
   347  				t.Error(err)
   348  			}
   349  			err = devfileData.AddComponents([]devfilev1.Component{
   350  				{
   351  					Name: buildContainerComponent,
   352  					ComponentUnion: devfilev1.ComponentUnion{
   353  						Container: &devfilev1.ContainerComponent{
   354  							Container: devfilev1.Container{},
   355  						},
   356  					},
   357  				},
   358  				{
   359  					Name: runContainerComponent,
   360  					ComponentUnion: devfilev1.ComponentUnion{
   361  						Container: &devfilev1.ContainerComponent{
   362  							Container: devfilev1.Container{},
   363  						},
   364  					},
   365  				},
   366  				{
   367  					Name: debugContainerComponent,
   368  					ComponentUnion: devfilev1.ComponentUnion{
   369  						Container: &devfilev1.ContainerComponent{
   370  							Container: devfilev1.Container{},
   371  						},
   372  					},
   373  				},
   374  			})
   375  			if err != nil {
   376  				t.Error(err)
   377  			}
   378  			err = devfileData.AddCommands([]devfilev1.Command{
   379  				{
   380  					Id: buildCommand,
   381  					CommandUnion: devfilev1.CommandUnion{
   382  						Exec: &devfilev1.ExecCommand{
   383  							CommandLine: buildCmdLine,
   384  							Component:   buildContainerComponent,
   385  							LabeledCommand: devfilev1.LabeledCommand{
   386  								BaseCommand: devfilev1.BaseCommand{
   387  									Group: &execBuildGroup,
   388  								},
   389  							},
   390  						},
   391  					},
   392  				},
   393  				{
   394  					Id: runCommand,
   395  					CommandUnion: devfilev1.CommandUnion{
   396  						Exec: &devfilev1.ExecCommand{
   397  							CommandLine: runCmdLine,
   398  							Component:   runContainerComponent,
   399  							LabeledCommand: devfilev1.LabeledCommand{
   400  								BaseCommand: devfilev1.BaseCommand{
   401  									Group: &execRunGroup,
   402  								},
   403  							},
   404  						},
   405  					},
   406  				},
   407  				{
   408  					Id: debugCommand,
   409  					CommandUnion: devfilev1.CommandUnion{
   410  						Exec: &devfilev1.ExecCommand{
   411  							CommandLine: debugCmdLine,
   412  							Component:   debugContainerComponent,
   413  							LabeledCommand: devfilev1.LabeledCommand{
   414  								BaseCommand: devfilev1.BaseCommand{
   415  									Group: &execDebugGroup,
   416  								},
   417  							},
   418  						},
   419  					},
   420  				},
   421  			})
   422  			if err != nil {
   423  				t.Error(err)
   424  			}
   425  			devfileObj := devfileParser.DevfileObj{
   426  				Data: devfileData,
   427  			}
   428  
   429  			containerForComponents := []corev1.Container{
   430  				{
   431  					Name:    buildContainerComponent,
   432  					Command: tt.buildContainerCommand,
   433  					Args:    tt.buildContainerArgs,
   434  				},
   435  				{
   436  					Name:    runContainerComponent,
   437  					Command: tt.runContainerCommand,
   438  					Args:    tt.runContainerArgs,
   439  				},
   440  				{
   441  					Name:    debugContainerComponent,
   442  					Command: tt.debugContainerCommand,
   443  					Args:    tt.debugContainerArgs,
   444  				},
   445  			}
   446  
   447  			containers, err := UpdateContainersEntrypointsIfNeeded(devfileObj, containerForComponents, tt.buildCommand, tt.runCommand, tt.debugCommand)
   448  			if tt.wantErr != (err != nil) {
   449  				t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
   450  				return
   451  			}
   452  
   453  			if len(containers) != len(tt.expectedContainerCommand) {
   454  				t.Errorf("length of expectedContainerCommand must match the one of containers, please fix test %q", tt.name)
   455  			}
   456  			if len(containers) != len(tt.expectedContainerArgs) {
   457  				t.Errorf("length of expectedContainerArgs must match the one of containers, please fix test %q", tt.name)
   458  			}
   459  			for _, c := range containers {
   460  				if len(c.Command) == 0 {
   461  					t.Errorf("empty command for container %q", c.Name)
   462  				}
   463  				if len(c.Args) == 0 {
   464  					t.Errorf("empty command for container %q", c.Args)
   465  				}
   466  
   467  				if diff := cmp.Diff(tt.expectedContainerCommand[c.Name], c.Command); diff != "" {
   468  					t.Errorf("UpdateContainersEntrypointsIfNeeded() expectedContainerCommand[%s] mismatch (-want +got):\n%s", c.Name, diff)
   469  				}
   470  				if diff := cmp.Diff(tt.expectedContainerArgs[c.Name], c.Args); diff != "" {
   471  					t.Errorf("UpdateContainersEntrypointsIfNeeded() expectedContainerArgs[%s] mismatch (-want +got):\n%s", c.Name, diff)
   472  				}
   473  			}
   474  
   475  		})
   476  	}
   477  }
   478  

View as plain text