Files
odo/pkg/libdevfile/libdevfile_test.go
Armel Soro 74516a9f58 Add support for command/args fields in container components (#5768)
* Introduce new 'pkg/remotecmd' package

This package allows to execute commands in remote packages
and exposes an interface for managing processes associated
to given Devfile commands.

* Rely on 'pkg/libdevfile' as much as possible for Devfile command execution

This requires passing a handler at the odo side,
which in turns uses the 'pkg/remotecmd' package to
run commands in remote containers.

* Switch to running without Supervisord as PID 1 in containers

To do this, the idea is to start the container component:
1- using the command/args defined in the Devfile
2- using whatever was defined in the container image
   if there is no command/args defined in the Devfile

Then, once the container is started, we would execute the Devfile
commands directly in the container component, just like a simple
'kubectl exec' command would do.
Since this is a long-running command (and potentially never ending),
we would need to run it in the background, i.e. in a side goroutine.

Point 2) above requires implementing a temporary hack (as discussed in [1]),
without us having to wait for [2] to be merged on the Devfile side.
This temporary hack overrides the container entrypoint with "tail -f /dev/null"
if the component defines no command or args (in which case we should have
used whatever is defined in the image, per the specification).

[1] https://github.com/redhat-developer/odo/pull/5768#issuecomment-1147190409
[2] https://github.com/devfile/registry/pull/102

* Rename K8s adapter struct 'client' field into 'kubeClient', as suggested in review

* Rename sync adapter struct 'client' fields to better distinguish between them

* Make sure messages displayed to users running 'odo dev' are the same

* Update temporary hack log message

Co-authored-by: Philippe Martin <contact@elol.fr>

* Make sure to handle process output line by line, for performance purposes

* Handle remote process output and errors in the Devfile command handler

The implementation in kubeexec.go should remain
as generic as possible

* Keep retrying remote process status until timeout, rather than just waiting for 1 sec

Now that the command is run via a goroutine,
there might be some situations where we were checking
the status just before the goroutine had a chance to start.

* Handle remote process output and errors in the Devfile command handler

The implementation in kubeexec.go should remain
as generic as possible

* Update kubeexec StopProcessForCommand implementation such that it relies on /proc to kill the parent children processes

* Ignore missing children file in getProcessChildren

* Unit-test methods in kubexec.go

* Fix missing logs when build command does not pass when running 'odo dev'

Also add integration test case

* Fix spinner status when commands passed to exec_handler do not pass

* Make sure to check process status right after stopping it

The process just stopped might take longer to exit (it might have caught
the signal and is performing additional cleanup)

* Keep retrying remote process status until timeout, rather than just waiting for 1 sec

Now that the command is run via a goroutine,
there might be some situations where we were checking
the status just before the goroutine had a chance to start.

* Fix potential deadlock when reading output from remotecmd#ExecuteCommandAndGetOutput

Rely on the same logic in ExecuteCommand

* Add more unit tests

* Remove block that used to check debug port from env info

As commented out in [1], we don't store anymore the debug port value in the ENV file.

[1] https://github.com/redhat-developer/odo/pull/5768#discussion_r893163382

* Rename 'getCommandFromFlag' into 'getCommandByName', as suggested in review

* Make remotecmd package more generic

This package no longer depends on Devfile-related packages.

* Fix comments in libdevfile.go

* Move errorIfTimeout struct field as parameter of RetryWithSchedule

This boolean is tied to the given retry schedule,
so it makes sense for it to be passed with the schedule.

* Expose a single ExecuteCommand function that returns both stdout and stderr

Co-authored-by: Philippe Martin <contact@elol.fr>
2022-06-17 10:51:02 +00:00

2565 lines
70 KiB
Go

package libdevfile
import (
"fmt"
"os"
"reflect"
"testing"
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/pkg/devfile/parser"
"github.com/devfile/library/pkg/devfile/parser/data"
devfileFileSystem "github.com/devfile/library/pkg/testingutil/filesystem"
dfutil "github.com/devfile/library/pkg/util"
"github.com/golang/mock/gomock"
"github.com/kylelemons/godebug/pretty"
"k8s.io/utils/pointer"
"github.com/redhat-developer/odo/pkg/libdevfile/generator"
"github.com/redhat-developer/odo/pkg/testingutil"
"github.com/redhat-developer/odo/pkg/util"
)
var buildGroup = v1alpha2.BuildCommandGroupKind
var runGroup = v1alpha2.RunCommandGroupKind
var debugGroup = v1alpha2.DebugCommandGroupKind
func TestGetDefaultCommand(t *testing.T) {
runDefault1 := generator.GetExecCommand(generator.ExecCommandParams{
Kind: v1alpha2.RunCommandGroupKind,
Id: "run-default-1",
IsDefault: pointer.BoolPtr(true),
})
deployDefault1 := generator.GetCompositeCommand(generator.CompositeCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "deploy-default-1",
IsDefault: pointer.BoolPtr(true),
})
deployDefault2 := generator.GetExecCommand(generator.ExecCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "deploy-default-2",
IsDefault: pointer.BoolPtr(true),
})
deployNoDefault1 := generator.GetApplyCommand(generator.ApplyCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "deploy-no-default-1",
IsDefault: pointer.BoolPtr(false),
})
deployUnspecDefault1 := generator.GetCompositeCommand(generator.CompositeCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "deploy-unspec-default-1",
IsDefault: nil,
})
type args struct {
devfileObj func() parser.DevfileObj
kind v1alpha2.CommandGroupKind
}
tests := []struct {
name string
args args
want v1alpha2.Command
wantErr error
}{
{
name: "a single deploy command, default",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1, deployDefault1})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: nil,
want: deployDefault1,
},
{
name: "a single deploy command, not default",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1, deployNoDefault1})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: nil,
want: deployNoDefault1,
},
{
name: "a single deploy command, unspecified default",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1, deployUnspecDefault1})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: nil,
want: deployUnspecDefault1,
},
{
name: "several deploy commands, only one is default",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1, deployDefault1, deployNoDefault1, deployUnspecDefault1})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: nil,
want: deployDefault1,
},
{
name: "no deploy command",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: NewNoCommandFoundError(v1alpha2.DeployCommandGroupKind),
},
{
name: "two deploy default commands",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1, deployDefault1, deployDefault2})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: NewMoreThanOneDefaultCommandFoundError(v1alpha2.DeployCommandGroupKind),
},
{
name: "two deploy commands, no one is default",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{runDefault1, deployNoDefault1, deployUnspecDefault1})
return parser.DevfileObj{
Data: data,
}
},
kind: v1alpha2.DeployCommandGroupKind,
},
wantErr: NewNoDefaultCommandFoundError(v1alpha2.DeployCommandGroupKind),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetDefaultCommand(tt.args.devfileObj(), tt.args.kind)
if err != tt.wantErr {
t.Errorf("GetDefaultCommand() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetDefaultCommand() = %v, want %v", got, tt.want)
}
})
}
}
func TestDeploy(t *testing.T) {
deployDefault1 := generator.GetCompositeCommand(generator.CompositeCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "deploy-default-1",
IsDefault: pointer.BoolPtr(true),
Commands: []string{"image-command", "deployment-command", "service-command"},
})
applyImageCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "image-command",
IsDefault: pointer.BoolPtr(false),
Component: "image-component",
})
applyDeploymentCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "deployment-command",
IsDefault: pointer.BoolPtr(false),
Component: "deployment-component",
})
applyServiceCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
Kind: v1alpha2.DeployCommandGroupKind,
Id: "service-command",
IsDefault: pointer.BoolPtr(false),
Component: "service-component",
})
imageComponent := generator.GetImageComponent(generator.ImageComponentParams{
Name: "image-component",
Image: v1alpha2.Image{
ImageName: "an-image-name",
},
})
deploymentComponent := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: "deployment-component",
Kubernetes: &v1alpha2.KubernetesComponent{},
})
serviceComponent := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: "service-component",
Kubernetes: &v1alpha2.KubernetesComponent{},
})
type args struct {
devfileObj func() parser.DevfileObj
handler func(ctrl *gomock.Controller) Handler
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "deploy an image and two kubernetes components",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddCommands([]v1alpha2.Command{deployDefault1, applyImageCommand, applyDeploymentCommand, applyServiceCommand})
_ = data.AddComponents([]v1alpha2.Component{imageComponent, deploymentComponent, serviceComponent})
return parser.DevfileObj{
Data: data,
}
},
handler: func(ctrl *gomock.Controller) Handler {
h := NewMockHandler(ctrl)
h.EXPECT().ApplyImage(imageComponent)
h.EXPECT().ApplyKubernetes(deploymentComponent)
h.EXPECT().ApplyKubernetes(serviceComponent)
return h
},
},
},
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
if err := Deploy(tt.args.devfileObj(), tt.args.handler(ctrl)); (err != nil) != tt.wantErr {
t.Errorf("Deploy() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestGetContainerEndpointMapping(t *testing.T) {
type args struct {
containers []v1alpha2.Component
}
imageComponent := generator.GetImageComponent(generator.ImageComponentParams{
Name: "image-component",
Image: v1alpha2.Image{
ImageName: "an-image-name",
},
})
containerWithNoEndpoints := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: "container 1",
Endpoints: nil,
})
containerWithOnePublicEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: "container 2",
Endpoints: []v1alpha2.Endpoint{
{
Name: "ep1",
TargetPort: 8080,
Exposure: v1alpha2.PublicEndpointExposure,
},
},
})
containerWithOneInternalEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: "container 3",
Endpoints: []v1alpha2.Endpoint{
{
Name: "ep2",
TargetPort: 9090,
Exposure: v1alpha2.InternalEndpointExposure,
},
},
})
tests := []struct {
name string
args args
want map[string][]int
}{
{
name: "invalid input - image components instead of container components",
args: args{
containers: []v1alpha2.Component{imageComponent},
},
want: map[string][]int{},
},
{
name: "one container with no endpoints exposed",
args: args{
containers: []v1alpha2.Component{containerWithNoEndpoints},
},
want: map[string][]int{containerWithNoEndpoints.Name: {}},
},
{
name: "multiple containers with varying types of endpoints",
args: args{
containers: []v1alpha2.Component{containerWithNoEndpoints, containerWithOnePublicEndpoint, containerWithOneInternalEndpoint},
},
want: map[string][]int{containerWithNoEndpoints.Name: {}, containerWithOnePublicEndpoint.Name: {8080}, containerWithOneInternalEndpoint.Name: {9090}},
},
{
name: "invalid input - one image component with rest being containers",
args: args{
containers: []v1alpha2.Component{containerWithNoEndpoints, containerWithOnePublicEndpoint, containerWithOneInternalEndpoint, imageComponent},
},
want: map[string][]int{containerWithNoEndpoints.Name: {}, containerWithOnePublicEndpoint.Name: {8080}, containerWithOneInternalEndpoint.Name: {9090}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetContainerEndpointMapping(tt.args.containers)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetContainerEndpointMapping() got = %v, want %v", got, tt.want)
}
})
}
}
func TestGetEndpointsFromDevfile(t *testing.T) {
type args struct {
devfileObj func() parser.DevfileObj
ignoreExposures []v1alpha2.EndpointExposure
}
ep1 := v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: v1alpha2.NoneEndpointExposure}
ep2 := v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: v1alpha2.InternalEndpointExposure}
ep3 := v1alpha2.Endpoint{Name: "ep3", TargetPort: 8888, Exposure: v1alpha2.PublicEndpointExposure}
container := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: "container-1",
Endpoints: []v1alpha2.Endpoint{ep1, ep2, ep3},
})
tests := []struct {
name string
args args
want []v1alpha2.Endpoint
wantErr bool
}{
{
name: "Ignore exposure of type none",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddComponents([]v1alpha2.Component{container})
return parser.DevfileObj{
Data: data,
}
},
ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.NoneEndpointExposure},
},
want: []v1alpha2.Endpoint{ep2, ep3},
},
{
name: "Ignore exposure of type public",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddComponents([]v1alpha2.Component{container})
return parser.DevfileObj{
Data: data,
}
},
ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.PublicEndpointExposure},
},
want: []v1alpha2.Endpoint{ep1, ep2},
},
{
name: "Ignore exposure of type internal",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddComponents([]v1alpha2.Component{container})
return parser.DevfileObj{
Data: data,
}
},
ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.InternalEndpointExposure},
},
want: []v1alpha2.Endpoint{ep1, ep3},
},
{
name: "Ignore none",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddComponents([]v1alpha2.Component{container})
return parser.DevfileObj{
Data: data,
}
},
ignoreExposures: []v1alpha2.EndpointExposure{},
},
want: []v1alpha2.Endpoint{ep1, ep2, ep3},
},
{
name: "Ignore all exposure types",
args: args{
devfileObj: func() parser.DevfileObj {
data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
_ = data.AddComponents([]v1alpha2.Component{container})
return parser.DevfileObj{
Data: data,
}
},
ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.InternalEndpointExposure, v1alpha2.NoneEndpointExposure, v1alpha2.PublicEndpointExposure},
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetEndpointsFromDevfile(tt.args.devfileObj(), tt.args.ignoreExposures)
if (err != nil) != tt.wantErr {
t.Errorf("GetEndpointsFromDevfile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetEndpointsFromDevfile() got = %v, want %v", got, tt.want)
}
})
}
}
func TestGetK8sManifestWithVariablesSubstituted(t *testing.T) {
fakeFs := devfileFileSystem.NewFakeFs()
cmpName := "my-cmp-1"
for _, tt := range []struct {
name string
setupFunc func() error
devfileObjFunc func() parser.DevfileObj
wantErr bool
want string
}{
{
name: "Missing Component",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: "a-different-component",
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
},
{
name: "Multiple Components with the same name",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp1 := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: cmpName,
})
cmp2 := generator.GetImageComponent(generator.ImageComponentParams{
Name: cmpName,
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp1, cmp2},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
},
{
name: "Container Component",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetContainerComponent(generator.ContainerComponentParams{
Name: cmpName,
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
},
{
name: "Image Component",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetImageComponent(generator.ImageComponentParams{
Name: cmpName,
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
},
{
name: "Kubernetes Component - Inlined with no variables",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Inlined: "some-text-inlined",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: false,
want: "some-text-inlined",
},
{
name: "Kubernetes Component - Inlined with variables",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Inlined: "image: {{MY_CONTAINER_IMAGE}}",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Variables: map[string]string{
"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image:1.2.3",
},
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: false,
want: "image: quay.io/unknown-account/my-image:1.2.3",
},
{
name: "Kubernetes Component - Inlined with unknown variables",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Inlined: "image: {{MY_CONTAINER_IMAGE}}:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Variables: map[string]string{
"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
},
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
want: "image: quay.io/unknown-account/my-image:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
},
{
name: "Kubernetes Component - non-existing external file",
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Uri: "kubernetes/my-external-file-with-should-not-exist",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
},
{
name: "Kubernetes Component - URI with no variables",
setupFunc: func() error {
return fakeFs.WriteFile("kubernetes/my-external-file",
[]byte("some-text-with-no-variables"),
os.ModePerm)
},
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Uri: "kubernetes/my-external-file",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: false,
want: "some-text-with-no-variables",
},
{
name: "Kubernetes Component - URI with variables",
setupFunc: func() error {
return fakeFs.WriteFile("kubernetes/my-deployment.yaml",
[]byte("image: {{ MY_CONTAINER_IMAGE }}:{{MY_CONTAINER_IMAGE_VERSION}}"),
os.ModePerm)
},
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Uri: "kubernetes/my-deployment.yaml",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Variables: map[string]string{
"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
"MY_CONTAINER_IMAGE_VERSION": "1.2.3",
},
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: false,
want: "image: quay.io/unknown-account/my-image:1.2.3",
},
{
name: "Kubernetes Component - URI with unknown variables",
setupFunc: func() error {
return fakeFs.WriteFile("kubernetes/my-external-file.yaml",
[]byte("image: {{MY_CONTAINER_IMAGE}}:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}"),
os.ModePerm)
},
devfileObjFunc: func() parser.DevfileObj {
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
Name: cmpName,
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Uri: "kubernetes/my-external-file.yaml",
},
},
},
})
s := v1alpha2.DevWorkspaceTemplateSpec{
DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
Variables: map[string]string{
"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
},
Components: []v1alpha2.Component{cmp},
},
}
devfileData.SetDevfileWorkspaceSpec(s)
return parser.DevfileObj{
Data: devfileData,
}
},
wantErr: true,
want: "image: quay.io/unknown-account/my-image:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
},
} {
t.Run(tt.name, func(t *testing.T) {
if tt.setupFunc != nil {
if err := tt.setupFunc(); err != nil {
t.Errorf("setup function returned an error: %v", err)
return
}
}
if tt.devfileObjFunc == nil {
t.Error("devfileObjFunc function not defined for test case")
return
}
got, err := GetK8sManifestWithVariablesSubstituted(tt.devfileObjFunc(), cmpName, "", fakeFs)
if (err != nil) != tt.wantErr {
t.Errorf("GetK8sManifestWithVariablesSubstituted() error = %v, wantErr %v",
err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GetK8sManifestWithVariablesSubstituted() got = %v, want %v",
got, tt.want)
}
})
}
}
func TestGetCommand(t *testing.T) {
commands := [...]string{"ls -la", "pwd"}
components := [...]string{"alias1", "alias2"}
tests := []struct {
name string
requestedType []v1alpha2.CommandGroupKind
execCommands []v1alpha2.Command
compCommands []v1alpha2.Command
reqCommandName string
retCommandName string
wantErr bool
}{
{
name: "Case 1: Valid devfile",
execCommands: []v1alpha2.Command{
getExecCommand("build", buildGroup),
getExecCommand("run", runGroup),
},
requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
wantErr: false,
},
{
name: "Case 2: Valid devfile with devrun and devbuild",
execCommands: []v1alpha2.Command{
getExecCommand("build", buildGroup),
getExecCommand("run", runGroup),
},
requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
wantErr: false,
},
{
name: "Case 3: Valid devfile with empty workdir",
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
requestedType: []v1alpha2.CommandGroupKind{runGroup},
wantErr: false,
},
{
name: "Case 4: Mismatched command type",
execCommands: []v1alpha2.Command{
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
reqCommandName: "build command",
requestedType: []v1alpha2.CommandGroupKind{buildGroup},
wantErr: true,
},
{
name: "Case 5: Default command is returned",
execCommands: []v1alpha2.Command{
{
Id: "defaultRunCommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "runCommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
retCommandName: "defaultRunCommand",
requestedType: []v1alpha2.CommandGroupKind{runGroup},
wantErr: false,
},
{
name: "Case 6: Composite command is returned",
execCommands: []v1alpha2.Command{
{
Id: "build",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(false)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "run",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
compCommands: []v1alpha2.Command{
{
Id: "myComposite",
CommandUnion: v1alpha2.CommandUnion{
Composite: &v1alpha2.CompositeCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(true)},
},
},
Commands: []string{"build", "run"},
},
},
},
},
retCommandName: "myComposite",
requestedType: []v1alpha2.CommandGroupKind{buildGroup},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.compCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents(components)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
for _, gtype := range tt.requestedType {
cmd, err := getCommand(devObj.Data, tt.reqCommandName, gtype)
if !tt.wantErr == (err != nil) {
t.Errorf("TestGetCommand unexpected error for command: %v wantErr: %v err: %v", gtype, tt.wantErr, err)
return
} else if tt.wantErr {
return
}
if len(tt.retCommandName) > 0 && cmd.Id != tt.retCommandName {
t.Errorf("TestGetCommand error: command names do not match expected: %v actual: %v", tt.retCommandName, cmd.Id)
}
}
})
}
}
func Test_getCommandAssociatedToGroup(t *testing.T) {
commands := [...]string{"ls -la", "pwd"}
components := [...]string{"alias1", "alias2"}
tests := []struct {
name string
requestedType []v1alpha2.CommandGroupKind
execCommands []v1alpha2.Command
compCommands []v1alpha2.Command
retCommandName string
wantErr bool
}{
{
name: "Case 1: Valid devfile",
execCommands: []v1alpha2.Command{
getExecCommand("", buildGroup),
getExecCommand("", runGroup),
},
requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
wantErr: false,
},
{
name: "Case 2: Valid devfile with devrun and devbuild",
execCommands: []v1alpha2.Command{
getExecCommand("", buildGroup),
getExecCommand("", runGroup),
},
requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
wantErr: false,
},
{
name: "Case 3: Valid devfile with empty workdir",
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
requestedType: []v1alpha2.CommandGroupKind{runGroup},
wantErr: false,
},
{
name: "Case 4: Default command is returned",
execCommands: []v1alpha2.Command{
{
Id: "defaultruncommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "runcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
retCommandName: "defaultruncommand",
requestedType: []v1alpha2.CommandGroupKind{runGroup},
wantErr: false,
},
{
name: "Case 5: Valid devfile, has composite command",
execCommands: []v1alpha2.Command{
{
Id: "build1",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "build2",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "run",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
compCommands: []v1alpha2.Command{
{
Id: "mycomp",
CommandUnion: v1alpha2.CommandUnion{
Composite: &v1alpha2.CompositeCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(true)},
},
},
Commands: []string{"build1", "run"},
},
},
},
},
retCommandName: "mycomp",
requestedType: []v1alpha2.CommandGroupKind{buildGroup},
wantErr: false,
},
{
name: "Case 6: Default composite command",
execCommands: []v1alpha2.Command{
{
Id: "build",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(false)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "run",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
compCommands: []v1alpha2.Command{
{
Id: "mycomp",
CommandUnion: v1alpha2.CommandUnion{
Composite: &v1alpha2.CompositeCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(true)},
},
},
Commands: []string{"build", "run"},
},
},
},
{
Id: "mycomp2",
CommandUnion: v1alpha2.CommandUnion{
Composite: &v1alpha2.CompositeCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(false)},
},
},
Commands: []string{"build", "run"},
},
},
},
},
retCommandName: "mycomp",
requestedType: []v1alpha2.CommandGroupKind{buildGroup},
wantErr: false,
},
{
name: "Case 7: no build and debug commands",
execCommands: []v1alpha2.Command{
getExecCommand("", runGroup),
},
requestedType: []v1alpha2.CommandGroupKind{buildGroup, debugGroup},
wantErr: false,
},
{
name: "Case 8: no default build and debug commands",
execCommands: []v1alpha2.Command{
getExecCommand("build-0", buildGroup),
getExecCommand("build-1", buildGroup),
getExecCommand("debug-0", debugGroup),
getExecCommand("debug-1", debugGroup),
getExecCommand("", runGroup),
},
requestedType: []v1alpha2.CommandGroupKind{buildGroup, debugGroup},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.compCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents(components)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
for _, gtype := range tt.requestedType {
cmd, err := getCommandAssociatedToGroup(devObj.Data, gtype)
if !tt.wantErr == (err != nil) {
t.Errorf("TestGetCommandFromDevfile unexpected error for command: %v wantErr: %v err: %v", gtype, tt.wantErr, err)
return
} else if tt.wantErr {
return
}
if len(tt.retCommandName) > 0 && cmd.Id != tt.retCommandName {
t.Errorf("TestGetCommandFromDevfile error: command names do not match expected: %v actual: %v", tt.retCommandName, cmd.Id)
}
}
})
}
}
func Test_getCommandByName(t *testing.T) {
commands := [...]string{"ls -la", "pwd"}
components := [...]string{"alias1", "alias2"}
invalidComponent := "garbagealias"
tests := []struct {
name string
requestedType v1alpha2.CommandGroupKind
execCommands []v1alpha2.Command
compCommands []v1alpha2.Command
reqCommandName string
retCommandName string
wantErr bool
}{
{
name: "Case 1: Valid devfile",
execCommands: []v1alpha2.Command{
getExecCommand("a", buildGroup),
getExecCommand("b", runGroup),
},
reqCommandName: "b",
retCommandName: "b",
requestedType: runGroup,
wantErr: false,
},
{
name: "Case 2: Valid devfile with empty workdir",
execCommands: []v1alpha2.Command{
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
reqCommandName: "build command",
retCommandName: "build command",
requestedType: runGroup,
wantErr: false,
},
{
name: "Case 3: Invalid command",
execCommands: []v1alpha2.Command{
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: invalidComponent,
},
},
},
},
reqCommandName: "build command wrong",
requestedType: runGroup,
wantErr: true,
},
{
name: "Case 4: Mismatched command type",
execCommands: []v1alpha2.Command{
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
reqCommandName: "build command",
requestedType: buildGroup,
wantErr: true,
},
{
name: "Case 5: Multiple default commands but should be with the flag",
execCommands: []v1alpha2.Command{
{
Id: "defaultruncommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "runcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
reqCommandName: "defaultruncommand",
retCommandName: "defaultruncommand",
requestedType: runGroup,
wantErr: false,
},
{
name: "Case 6: No default command but should be with the flag",
execCommands: []v1alpha2.Command{
{
Id: "defaultruncommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "runcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
reqCommandName: "defaultruncommand",
retCommandName: "defaultruncommand",
requestedType: runGroup,
wantErr: false,
},
{
name: "Case 7: No Command Group",
execCommands: []v1alpha2.Command{
{
Id: "defaultruncommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
CommandLine: commands[0],
Component: components[0],
},
},
},
},
reqCommandName: "defaultruncommand",
retCommandName: "defaultruncommand",
requestedType: runGroup,
wantErr: false,
},
{
name: "Case 8: Valid devfile with composite commands",
execCommands: []v1alpha2.Command{
{
Id: "build",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(false)},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
{
Id: "run",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: commands[0],
Component: components[0],
},
},
},
},
compCommands: []v1alpha2.Command{
{
Id: "mycomp",
CommandUnion: v1alpha2.CommandUnion{
Composite: &v1alpha2.CompositeCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(true)},
},
},
Commands: []string{"build", "run"},
},
},
},
{
Id: "mycomp2",
CommandUnion: v1alpha2.CommandUnion{
Composite: &v1alpha2.CompositeCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(false)},
},
},
Commands: []string{"build", "run"},
},
},
},
},
reqCommandName: "mycomp",
retCommandName: "mycomp",
requestedType: buildGroup,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
if tt.execCommands[0].Exec.Component == invalidComponent {
components = []v1alpha2.Component{testingutil.GetFakeContainerComponent("randomComponent")}
}
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.compCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents(components)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
cmd, err := getCommandByName(devObj.Data, tt.requestedType, tt.reqCommandName)
if !tt.wantErr == (err != nil) {
t.Errorf("TestGetCommand unexpected error for command: %v wantErr: %v err: %v", tt.requestedType, tt.wantErr, err)
return
} else if tt.wantErr {
return
}
if cmd.Exec != nil {
if cmd.Id != tt.retCommandName {
t.Errorf("TestGetCommand error: command names do not match expected: %v actual: %v", tt.retCommandName, cmd.Id)
}
}
})
}
}
func TestGetBuildCommand(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
tests := []struct {
name string
commandName string
execCommands []v1alpha2.Command
wantCommand v1alpha2.Command
wantErr bool
}{
{
name: "Case 1: Default Build Command",
commandName: emptyString,
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantCommand: v1alpha2.Command{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
wantErr: false,
},
{
name: "Case 2: Build Command passed through the odo flag",
commandName: "flagcommand",
execCommands: []v1alpha2.Command{
{
Id: "flagcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantCommand: v1alpha2.Command{
Id: "flagcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
wantErr: false,
},
{
name: "Case 3: Build Command not found",
commandName: "customcommand123",
execCommands: []v1alpha2.Command{
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)})
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
command, err := GetBuildCommand(devObj.Data, tt.commandName)
if !tt.wantErr == (err != nil) {
t.Errorf("TestGetBuildCommand: unexpected error for command \"%v\" expected: %v actual: %v", tt.commandName, tt.wantErr, err)
} else if !tt.wantErr && !reflect.DeepEqual(tt.wantCommand, command) {
t.Errorf("TestGetBuildCommand: unexpected command returned: %v", pretty.Compare(tt.wantCommand, command))
}
})
}
}
func TestGetDebugCommand(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
var emptyCommand v1alpha2.Command
tests := []struct {
name string
commandName string
execCommands []v1alpha2.Command
wantErr bool
}{
{
name: "Case: Default Debug Command",
commandName: emptyString,
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(true),
Kind: v1alpha2.DebugCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: false,
},
{
name: "Case: Custom Debug Command",
commandName: "customdebugcommand",
execCommands: []v1alpha2.Command{
{
Id: "customdebugcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(false),
Kind: v1alpha2.DebugCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: false,
},
{
name: "Case: Missing Debug Command",
commandName: "customcommand123",
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(true),
Kind: v1alpha2.BuildCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)})
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
command, err := GetDebugCommand(devObj.Data, tt.commandName)
if tt.wantErr && err == nil {
t.Errorf("Error was expected but got no error")
} else if !tt.wantErr {
if err != nil {
t.Errorf("TestGetDebugCommand: unexpected error for command \"%v\" expected: %v actual: %v", tt.commandName, tt.wantErr, err)
} else if reflect.DeepEqual(emptyCommand, command) {
t.Errorf("TestGetDebugCommand: unexpected empty command returned for command: %v", tt.commandName)
}
}
})
}
}
func TestGetTestCommand(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
var emptyCommand v1alpha2.Command
tests := []struct {
name string
commandName string
execCommands []v1alpha2.Command
wantErr bool
}{
{
name: "Case: Default Test Command",
commandName: emptyString,
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(true),
Kind: v1alpha2.TestCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: false,
},
{
name: "Case: Custom Test Command",
commandName: "customtestcommand",
execCommands: []v1alpha2.Command{
{
Id: "customtestcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(false),
Kind: v1alpha2.TestCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: false,
},
{
name: "Case: Missing Test Command",
commandName: "customcommand123",
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(true),
Kind: v1alpha2.BuildCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)})
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
command, err := GetTestCommand(devObj.Data, tt.commandName)
if tt.wantErr && err == nil {
t.Errorf("Error was expected but got no error")
} else if !tt.wantErr {
if err != nil {
t.Errorf("TestGetTestCommand: unexpected error for command \"%v\" expected: %v actual: %v", tt.commandName, tt.wantErr, err)
} else if reflect.DeepEqual(emptyCommand, command) {
t.Errorf("TestGetTestCommand: unexpected empty command returned for command: %v", tt.commandName)
}
}
})
}
}
func TestGetRunCommand(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
var emptyCommand v1alpha2.Command
tests := []struct {
name string
commandName string
execCommands []v1alpha2.Command
wantErr bool
}{
{
name: "Case 1: Default Run Command",
commandName: emptyString,
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBoolPtr(true)},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: false,
},
{
name: "Case 2: Run Command passed through odo flag",
commandName: "flagcommand",
execCommands: []v1alpha2.Command{
{
Id: "flagcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
{
Id: "run command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: false,
},
{
name: "Case 3: Missing Run Command",
commandName: "",
execCommands: []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)})
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
command, err := GetRunCommand(devObj.Data, tt.commandName)
if !tt.wantErr == (err != nil) {
t.Errorf("TestGetRunCommand: unexpected error for command \"%v\" expected: %v actual: %v", tt.commandName, tt.wantErr, err)
} else if !tt.wantErr && reflect.DeepEqual(emptyCommand, command) {
t.Errorf("TestGetRunCommand: unexpected empty command returned for command: %v", tt.commandName)
}
})
}
}
func TestValidateAndGetDebugCommands(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
execCommands := []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(true),
Kind: v1alpha2.DebugCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
{
Id: "customdebugcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(false),
Kind: v1alpha2.DebugCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
}
tests := []struct {
name string
debugCommand string
componentType v1alpha2.ComponentType
wantErr bool
}{
{
name: "Case: Default Devfile Commands",
debugCommand: emptyString,
componentType: v1alpha2.ContainerComponentType,
wantErr: false,
},
{
name: "Case: provided debug Command",
debugCommand: "customdebugcommand",
componentType: v1alpha2.ContainerComponentType,
wantErr: false,
},
{
name: "Case: invalid debug Command",
debugCommand: "invaliddebugcommand",
componentType: v1alpha2.ContainerComponentType,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)})
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(execCommands)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
debugCommand, err := ValidateAndGetDebugCommands(devObj.Data, tt.debugCommand)
if tt.wantErr {
if err == nil {
t.Errorf("Error was expected but got no error")
} else {
return
}
} else {
if err != nil {
t.Errorf("TestValidateAndGetDebugDevfileCommands: unexpected error %v", err)
}
}
if !reflect.DeepEqual(nil, debugCommand) && debugCommand.Id != tt.debugCommand {
t.Errorf("TestValidateAndGetDebugDevfileCommands name of debug command is wrong want: %v got: %v", tt.debugCommand, debugCommand.Id)
}
})
}
}
func TestValidateAndGetPushCommands(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
execCommands := []v1alpha2.Command{
{
Id: "run command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
Kind: runGroup,
IsDefault: util.GetBoolPtr(true),
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
{
Id: "build command",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: buildGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
{
Id: "customcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
}
wrongCompTypeCmd := v1alpha2.Command{
Id: "wrong",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
CommandLine: command,
Component: "",
WorkingDir: workDir,
},
},
}
tests := []struct {
name string
buildCommand string
runCommand string
execCommands []v1alpha2.Command
numberOfCommands int
missingBuildCommand bool
wantErr bool
}{
{
name: "Case 1: Default Devfile Commands",
buildCommand: emptyString,
runCommand: emptyString,
execCommands: execCommands,
numberOfCommands: 2,
wantErr: false,
},
{
name: "Case 2: Default Build Command, and Provided Run Command",
buildCommand: emptyString,
runCommand: "customcommand",
execCommands: execCommands,
numberOfCommands: 2,
wantErr: false,
},
{
name: "Case 3: Empty Component",
buildCommand: "customcommand",
runCommand: "customcommand",
execCommands: append(execCommands, wrongCompTypeCmd),
numberOfCommands: 0,
wantErr: true,
},
{
name: "Case 4: Provided Wrong Build Command and Provided Run Command",
buildCommand: "customcommand123",
runCommand: "customcommand",
execCommands: execCommands,
numberOfCommands: 1,
wantErr: true,
},
{
name: "Case 5: Missing Build Command, and Provided Run Command",
buildCommand: emptyString,
runCommand: "customcommand",
execCommands: []v1alpha2.Command{
{
Id: "customcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: runGroup},
},
},
Component: component,
CommandLine: command,
},
},
},
},
numberOfCommands: 1,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(tt.execCommands)
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents(([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)}))
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
pushCommands, err := ValidateAndGetPushCommands(devObj.Data, tt.buildCommand, tt.runCommand)
if !tt.wantErr == (err != nil) {
t.Errorf("TestValidateAndGetPushDevfileCommands unexpected error when validating commands wantErr: %v err: %v", tt.wantErr, err)
} else if tt.wantErr && err != nil {
return
}
if len(pushCommands) != tt.numberOfCommands {
t.Errorf("TestValidateAndGetPushDevfileCommands error: wrong number of validated commands expected: %v actual :%v", tt.numberOfCommands, len(pushCommands))
}
})
}
}
func TestValidateAndGetTestCommands(t *testing.T) {
command := "ls -la"
component := "alias1"
workDir := "/"
emptyString := ""
execCommands := []v1alpha2.Command{
{
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(true),
Kind: v1alpha2.TestCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
{
Id: "customtestcommand",
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{
IsDefault: util.GetBoolPtr(false),
Kind: v1alpha2.TestCommandGroupKind,
},
},
},
CommandLine: command,
Component: component,
WorkingDir: workDir,
},
},
},
}
tests := []struct {
name string
testCommand string
componentType v1alpha2.ComponentType
wantErr bool
}{
{
name: "Case: Default Devfile Commands",
testCommand: emptyString,
componentType: v1alpha2.ContainerComponentType,
wantErr: false,
},
{
name: "Case: provided test Command",
testCommand: "customtestcommand",
componentType: v1alpha2.ContainerComponentType,
wantErr: false,
},
{
name: "Case: invalid test Command",
testCommand: "invalidtestcommand",
componentType: v1alpha2.ContainerComponentType,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := parser.DevfileObj{
Data: func() data.DevfileData {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
if err != nil {
t.Error(err)
}
err = devfileData.AddComponents([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)})
if err != nil {
t.Error(err)
}
err = devfileData.AddCommands(execCommands)
if err != nil {
t.Error(err)
}
return devfileData
}(),
}
testCommand, err := ValidateAndGetTestCommands(devObj.Data, tt.testCommand)
if tt.wantErr {
if err == nil {
t.Errorf("Error was expected but got no error")
} else {
return
}
} else {
if err != nil {
t.Errorf("TestValidateAndGetTestDevfileCommands: unexpected error %v", err)
}
}
if !reflect.DeepEqual(nil, testCommand) && testCommand.Id != tt.testCommand {
t.Errorf("TestValidateAndGetTestDevfileCommands name of test command is wrong want: %v got: %v", tt.testCommand, testCommand.Id)
}
})
}
}
func TestShouldExecCommandRunOnContainer(t *testing.T) {
for _, tt := range []struct {
name string
execCommand *v1alpha2.ExecCommand
container string
want bool
}{
{
name: "nil exec command",
},
{
name: "exec component not matching container name",
execCommand: &v1alpha2.ExecCommand{
Component: "runtime",
},
container: "my-cont",
want: false,
},
{
name: "exec component matching container name",
execCommand: &v1alpha2.ExecCommand{
Component: "runtime",
},
container: "runtime",
want: true,
},
} {
t.Run(tt.name, func(t *testing.T) {
got := ShouldExecCommandRunOnContainer(tt.execCommand, tt.container)
if tt.want != got {
t.Errorf("expected=%v, got %v", tt.want, got)
}
})
}
}
func getExecCommand(id string, group v1alpha2.CommandGroupKind) v1alpha2.Command {
if len(id) == 0 {
id = fmt.Sprintf("%s-%s", "cmd", dfutil.GenerateRandomString(10))
}
commands := [...]string{"ls -la", "pwd"}
components := [...]string{"alias1", "alias2"}
workDir := [...]string{"/", "/root"}
return v1alpha2.Command{
Id: id,
CommandUnion: v1alpha2.CommandUnion{
Exec: &v1alpha2.ExecCommand{
LabeledCommand: v1alpha2.LabeledCommand{
BaseCommand: v1alpha2.BaseCommand{
Group: &v1alpha2.CommandGroup{Kind: group},
},
},
CommandLine: commands[0],
Component: components[0],
WorkingDir: workDir[0],
},
},
}
}