Files
odo/pkg/libdevfile/component_kubernetes_test.go
Armel Soro cc2e8def81 Support autoBuild and deployByDefault on Image and Kubernetes/OpenShift components (#6654)
* Add sample Devfile with multiple autoBuild/deployByDefault combinations

It will be used for integration tests.

* Add helper function to update a given Devfile Command Group

This will allow supporting cases where we need to run a custom command,
but this is not implemented yet on odo (cases with 'odo dev --debug'
and 'odo deploy').
In this case, this helper will allow updating the Devfile for example to
unmark the current default command as non-default, and mark the custom
one as default.

* Add test cases for 'odo dev'

* Add test cases for 'odo deploy'

* Add test cases for 'odo build-images'

'odo build-images' should build all images regardless of the 'autoBuild' property.

* Display the spinner when creating or updating Kubernetes resources

This helps understand what resources are being patched.

* Handle deployByDefault on K8s and OpenShift components

* Add 'devfile.GetImageComponentsToPush' functions that returns the list of image components to auto-create

See https://github.com/devfile/api/issues/852#issuecomment-1211928487 for more context.

* Handle automatic image component creation for 'odo dev' on Kubernetes

* Handle automatic image component creation for 'odo dev' on Podman

* Handle automatic image and K8s/OpenShift component creation for 'odo deploy'

* Bump Devfile library to the latest commit at this time (c1b23d2)

This includes the fix for [1], which provides a way not to set default values automatically

[1] https://github.com/devfile/api/issues/1067

* Do not set default values when parsing a Devfile

See [1] for the rationale

[1] https://github.com/redhat-developer/odo/issues/5694\#issuecomment-1465778398

* Add documentation in the Devfile reference page

* Revert "Display the spinner when creating or updating Kubernetes resources"

This reverts commit 6ad073e63cb0e685f165eed767619a90997393a3.

* Avoid re-applying Image components multiple times

'adapter#Push' might get called several times when there are
state transitions (either on the Deployment in the cluster,
or from 'odo').
It might be confusing to apply Image components over and over again
(and also it can be slower if we need to push images to remote registries).

* Move GetK8sAndOcComponentsToPush and GetImageComponentsToPush to libdevfile package

As suggested in review, this should be the responsibility of the devfile library

Co-authored-by: Philippe Martin <phmartin@redhat.com>

* fixup! Handle automatic image and K8s/OpenShift component creation for 'odo deploy'

* fixup! Handle automatic image component creation for 'odo dev' on Podman

* fixup! Avoid re-applying Image components multiple times

* Apply suggestions from code review

Co-authored-by: Parthvi Vala <pvala@redhat.com>

* fixup! Do not set default values when parsing a Devfile

* Fix devfile-deploy-functional-pods.yaml (used in 'odo logs' tests)

Set deployByDefault to false on innerloop-pod component.
Otherwise, since it is not referenced by any apply command,
it will be automatically created by *both* 'odo dev' and 'odo deploy'.

---------

Co-authored-by: Philippe Martin <phmartin@redhat.com>
Co-authored-by: Parthvi Vala <pvala@redhat.com>
2023-04-05 01:30:30 -04:00

255 lines
8.0 KiB
Go

package libdevfile
import (
"testing"
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
devfilepkg "github.com/devfile/api/v2/pkg/devfile"
"github.com/devfile/library/v2/pkg/devfile/parser"
devfileCtx "github.com/devfile/library/v2/pkg/devfile/parser/context"
"github.com/devfile/library/v2/pkg/devfile/parser/data"
devfileFileSystem "github.com/devfile/library/v2/pkg/testingutil/filesystem"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"k8s.io/utils/pointer"
devfiletesting "github.com/redhat-developer/odo/pkg/devfile/testing"
)
func TestGetK8sAndOcComponentsToPush(t *testing.T) {
fs := devfileFileSystem.NewFakeFs()
buildK8sOrOcComponent := func(k8s bool, name string, deployByDefault *bool, referenced bool) (v1alpha2.Component, v1alpha2.Command) {
k8sLikeComponent := v1alpha2.K8sLikeComponent{
DeployByDefault: deployByDefault,
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Inlined: name,
},
}
comp := v1alpha2.Component{Name: name}
if k8s {
comp.ComponentUnion.Kubernetes = &v1alpha2.KubernetesComponent{K8sLikeComponent: k8sLikeComponent}
} else {
comp.ComponentUnion.Openshift = &v1alpha2.OpenshiftComponent{K8sLikeComponent: k8sLikeComponent}
}
if referenced {
cmd := v1alpha2.Command{
Id: "apply-" + name,
CommandUnion: v1alpha2.CommandUnion{
Apply: &v1alpha2.ApplyCommand{
Component: name,
},
},
}
return comp, cmd
}
return comp, v1alpha2.Command{}
}
var (
k8sDeployByDefaultTrueReferenced, applyK8sDeployByDefaultTrueReferenced = buildK8sOrOcComponent(
true, "k8sDeployByDefaultTrueReferenced", pointer.Bool(true), true)
ocDeployByDefaultTrueReferenced, applyOcDeployByDefaultTrueReferenced = buildK8sOrOcComponent(
false, "ocDeployByDefaultTrueReferenced", pointer.Bool(true), true)
k8sDeployByDefaultTrueNotReferenced, _ = buildK8sOrOcComponent(
true, "k8sDeployByDefaultTrueNotReferenced", pointer.Bool(true), false)
ocDeployByDefaultTrueNotReferenced, _ = buildK8sOrOcComponent(
false, "ocDeployByDefaultTrueNotReferenced", pointer.Bool(true), false)
k8sDeployByDefaultFalseReferenced, applyK8sDeployByDefaultFalseReferenced = buildK8sOrOcComponent(
true, "k8sDeployByDefaultFalseReferenced", pointer.Bool(false), true)
ocDeployByDefaultFalseReferenced, applyOcDeployByDefaultFalseReferenced = buildK8sOrOcComponent(
false, "ocDeployByDefaultFalseReferenced", pointer.Bool(false), true)
k8sDeployByDefaultFalseNotReferenced, _ = buildK8sOrOcComponent(
true, "k8sDeployByDefaultFalseNotReferenced", pointer.Bool(false), false)
ocDeployByDefaultFalseNotReferenced, _ = buildK8sOrOcComponent(
false, "ocDeployByDefaultFalseNotReferenced", pointer.Bool(false), false)
k8sDeployByDefaultNotSetReferenced, applyK8sDeployByDefaultNotSetReferenced = buildK8sOrOcComponent(
true, "k8sDeployByDefaultNotSetReferenced", nil, true)
ocDeployByDefaultNotSetReferenced, applyOcDeployByDefaultNotSetReferenced = buildK8sOrOcComponent(
false, "ocDeployByDefaultNotSetReferenced", nil, true)
k8sDeployByDefaultNotSetNotReferenced, _ = buildK8sOrOcComponent(
true, "k8sDeployByDefaultNotSetNotReferenced", nil, false)
ocDeployByDefaultNotSetNotReferenced, _ = buildK8sOrOcComponent(
false, "ocDeployByDefaultNotSetNotReferenced", nil, false)
)
buildFullDevfile := func() (parser.DevfileObj, error) {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion220))
if err != nil {
return parser.DevfileObj{}, err
}
devfileData.SetMetadata(devfilepkg.DevfileMetadata{Name: "my-devfile"})
err = devfileData.AddComponents([]v1alpha2.Component{
k8sDeployByDefaultNotSetNotReferenced,
k8sDeployByDefaultNotSetReferenced,
ocDeployByDefaultNotSetReferenced,
ocDeployByDefaultNotSetNotReferenced,
k8sDeployByDefaultTrueNotReferenced,
k8sDeployByDefaultTrueReferenced,
ocDeployByDefaultTrueReferenced,
ocDeployByDefaultTrueNotReferenced,
k8sDeployByDefaultFalseNotReferenced,
k8sDeployByDefaultFalseReferenced,
ocDeployByDefaultFalseReferenced,
ocDeployByDefaultFalseNotReferenced,
//Add other kinds of components
{
Name: "my-image-component",
ComponentUnion: v1alpha2.ComponentUnion{
Image: &v1alpha2.ImageComponent{
Image: v1alpha2.Image{
ImageName: "image-component-1",
},
},
},
},
{
Name: "container-component",
ComponentUnion: v1alpha2.ComponentUnion{
Container: &v1alpha2.ContainerComponent{
Container: v1alpha2.Container{
DedicatedPod: pointer.Bool(true),
Image: "my-container-image",
},
},
},
},
})
if err != nil {
return parser.DevfileObj{}, err
}
err = devfileData.AddCommands([]v1alpha2.Command{
applyK8sDeployByDefaultNotSetReferenced,
applyOcDeployByDefaultNotSetReferenced,
applyK8sDeployByDefaultTrueReferenced,
applyOcDeployByDefaultTrueReferenced,
applyK8sDeployByDefaultFalseReferenced,
applyOcDeployByDefaultFalseReferenced,
//Add other kinds of components
{
Id: "apply-image",
CommandUnion: v1alpha2.CommandUnion{
Apply: &v1alpha2.ApplyCommand{
Component: "my-image-component",
},
},
},
{
Id: "exec-command",
CommandUnion: v1alpha2.CommandUnion{
Apply: &v1alpha2.ApplyCommand{
Component: "my-image-component",
},
Exec: &v1alpha2.ExecCommand{
CommandLine: "/path/to/my/command -success",
Component: "container-component",
},
},
},
})
if err != nil {
return parser.DevfileObj{}, err
}
return parser.DevfileObj{
Ctx: devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath),
Data: devfileData,
}, nil
}
type args struct {
devfileObj func() (parser.DevfileObj, error)
allowApply bool
}
tests := []struct {
name string
args args
want []v1alpha2.Component
wantErr bool
}{
{
name: "empty devfile",
args: args{
devfileObj: func() (parser.DevfileObj, error) {
return parser.DevfileObj{
Data: devfiletesting.GetDevfileData(t, nil, nil),
Ctx: devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath),
}, nil
},
},
want: []v1alpha2.Component{},
wantErr: false,
},
{
name: "allowApply=false => return components that need to be created automatically on startup",
args: args{
devfileObj: buildFullDevfile,
allowApply: false,
},
want: []v1alpha2.Component{
k8sDeployByDefaultTrueNotReferenced,
k8sDeployByDefaultTrueReferenced,
ocDeployByDefaultTrueNotReferenced,
ocDeployByDefaultTrueReferenced,
k8sDeployByDefaultNotSetNotReferenced,
ocDeployByDefaultNotSetNotReferenced,
},
},
{
name: "allowApply=true => return components that need to be created automatically on startup and those referenced",
args: args{
devfileObj: buildFullDevfile,
allowApply: true,
},
want: []v1alpha2.Component{
k8sDeployByDefaultTrueNotReferenced,
k8sDeployByDefaultTrueReferenced,
ocDeployByDefaultTrueNotReferenced,
ocDeployByDefaultTrueReferenced,
k8sDeployByDefaultNotSetNotReferenced,
ocDeployByDefaultNotSetNotReferenced,
k8sDeployByDefaultFalseReferenced,
ocDeployByDefaultFalseReferenced,
k8sDeployByDefaultNotSetReferenced,
ocDeployByDefaultNotSetReferenced,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devfileObj, err := tt.args.devfileObj()
if err != nil {
t.Errorf("unable to create Devfile object: %v", err)
return
}
got, err := GetK8sAndOcComponentsToPush(devfileObj, tt.args.allowApply)
gotErr := err != nil
if gotErr != tt.wantErr {
t.Errorf("Got error %v, expected %v\n", err, tt.wantErr)
}
if len(got) != len(tt.want) {
t.Errorf("Got %d components, expected %d\n", len(got), len(tt.want))
}
lessFn := func(x, y v1alpha2.Component) bool {
return x.Name < y.Name
}
if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty(), cmpopts.SortSlices(lessFn)); diff != "" {
t.Errorf("GetK8sAndOcComponentsToPush() mismatch (-want +got):\n%s", diff)
}
})
}
}