diff --git a/pkg/alizer/alizer.go b/pkg/alizer/alizer.go index 0316e83348c..0bc863ebbd4 100644 --- a/pkg/alizer/alizer.go +++ b/pkg/alizer/alizer.go @@ -1,9 +1,15 @@ package alizer import ( + "fmt" + "os" + "path/filepath" + "github.com/redhat-developer/alizer/go/pkg/apis/recognizer" "github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/registry" + "github.com/redhat-developer/odo/pkg/util" + "k8s.io/klog" ) type Alizer struct { @@ -47,3 +53,65 @@ func GetDevfileLocationFromDetection(typ recognizer.DevFileType, registry api.Re DevfileRegistry: registry.Name, } } + +// DetectName retrieves the name of the project (if available) +// If source code is detected: +// 1. Detect the name (pom.xml for java, package.json for nodejs, etc.) +// 2. If unable to detect the name, use the directory name +// +// If no source is detected: +// 1. Use the directory name +// +// Last step. Sanitize the name so it's valid for a component name + +// Use: +// import "github.com/redhat-developer/alizer/pkg/apis/recognizer" +// components, err := recognizer.DetectComponents("./") + +// In order to detect the name, the name will first try to find out the name based on the program (pom.xml, etc.) but then if not, it will use the dir name. +func DetectName(path string) (string, error) { + if path == "" { + return "", fmt.Errorf("path is empty") + } + + // Check if the path exists using os.Stat + _, err := os.Stat(path) + if err != nil { + return "", err + } + + // Step 1. + // Get the name of the directory from the devfile absolute path + // Use that path with Alizer to get the name of the project, + // if unable to find the name, we will use the directory name + components, err := recognizer.DetectComponents(path) + if err != nil { + return "", err + } + klog.V(4).Infof("Found components: %v", components) + + // Take the first name that is found + var detectedName string + if len(components) > 0 { + detectedName = components[0].Name + } + + // Step 2. If unable to detect the name, we will use the directory name. + // Alizer will not correctly default to the directory name when unable to detect it via pom.xml, package.json, etc. + // So we must do it ourselves + if detectedName == "" { + // Using filepath.Dir may be redundant, but it's to also check in case we passed in a filename (which shouldn't be passed in anyways) + detectedName = filepath.Base(filepath.Dir(path)) + } + + // Step 3. Sanitize the name + // Make sure that detectedName conforms with Kubernetes naming rules + // If not, we will use the directory name + name := util.GetDNS1123Name(detectedName) + klog.V(4).Infof("Path: %s, Detected name: %s, Sanitized name: %s", path, detectedName, name) + if name == "" { + return "", fmt.Errorf("unable to sanitize name to DNS1123 format: %q", name) + } + + return name, nil +} diff --git a/pkg/binding/add.go b/pkg/binding/add.go index c34a878fa49..9ec458f5748 100644 --- a/pkg/binding/add.go +++ b/pkg/binding/add.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/redhat-developer/odo/pkg/component" sboApi "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1" "github.com/redhat-developer/odo/pkg/binding/asker" @@ -112,7 +113,12 @@ func (o *BindingClient) AddBindingToDevfile( return obj, err } - deploymentName := fmt.Sprintf("%s-app", obj.GetMetadataName()) + componentName, err := component.GatherName(obj) + if err != nil { + return obj, err + } + + deploymentName := fmt.Sprintf("%s-app", componentName) deploymentGVK, err := o.kubernetesClient.GetDeploymentAPIVersion() if err != nil { return obj, err diff --git a/pkg/component/component.go b/pkg/component/component.go index e56cb523ead..96498bee506 100644 --- a/pkg/component/component.go +++ b/pkg/component/component.go @@ -3,7 +3,6 @@ package component import ( "fmt" "io" - "path/filepath" "sort" "strings" @@ -12,15 +11,17 @@ import ( "github.com/devfile/library/pkg/devfile/parser" "github.com/devfile/library/pkg/devfile/parser/data" dfutil "github.com/devfile/library/pkg/util" + "k8s.io/klog" + "github.com/redhat-developer/odo/pkg/alizer" "github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/kclient" "github.com/redhat-developer/odo/pkg/labels" odolabels "github.com/redhat-developer/odo/pkg/labels" + "github.com/redhat-developer/odo/pkg/util" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/klog" ) const ( @@ -42,32 +43,23 @@ func GetComponentTypeFromDevfileMetadata(metadata devfile.DevfileMetadata) strin return componentType } -// GatherName parses the Devfile and retrieves an appropriate name in two ways. -// 1. If metadata.name exists, we use it -// 2. If metadata.name does NOT exist, we use the folder name where the devfile.yaml is located -func GatherName(devObj parser.DevfileObj, devfilePath string) (string, error) { - - metadata := devObj.Data.GetMetadata() - - klog.V(4).Infof("metadata.Name: %s", metadata.Name) - - // 1. Use metadata.name if it exists - if metadata.Name != "" { - - // Remove any suffix's that end with `-`. This is because many Devfile's use the original v1 Devfile pattern of - // having names such as "foo-bar-" in order to prepend container names such as "foo-bar-container1" - return strings.TrimSuffix(metadata.Name, "-"), nil +// GatherName returns the name of the component. +//// If a name is set in the Devfile metadata (which is optional) and is not blank, it returns that. +//// Otherwise, it uses Alizer to detect the name of the component, from the project build tools (pom.xml, package.json, ...), +//// or from the component directory name. +func GatherName(devfileObj parser.DevfileObj) (string, error) { + devfilePath := devfileObj.Ctx.GetAbsPath() + name := devfileObj.GetMetadataName() + if name == "" || strings.TrimSpace(name) == "" { + //name already sanitized in alizer.DetectName + return alizer.DetectName(devfilePath) } - // 2. Use the folder name as a last resort if nothing else exists - sourcePath, err := dfutil.GetAbsPath(devfilePath) - if err != nil { - return "", fmt.Errorf("unable to get source path: %w", err) - } - klog.V(4).Infof("Source path: %s", sourcePath) - klog.V(4).Infof("devfile dir: %s", filepath.Dir(sourcePath)) + //sanitize the name + s := util.GetDNS1123Name(name) + klog.V(3).Infof("For Devfile at path %q, name of component is %q, and sanitized name is %q", devfilePath, name, s) - return filepath.Base(filepath.Dir(sourcePath)), nil + return s, nil } // Exists checks whether a component with the given name exists in the current application or not diff --git a/pkg/component/component_test.go b/pkg/component/component_test.go index fcc8b98c845..afda5337ab6 100644 --- a/pkg/component/component_test.go +++ b/pkg/component/component_test.go @@ -2,18 +2,30 @@ package component import ( "errors" + "os" + "path" + "path/filepath" "reflect" "testing" devfilepkg "github.com/devfile/api/v2/pkg/devfile" + "github.com/devfile/library/pkg/devfile" + "github.com/devfile/library/pkg/devfile/parser" + devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" + "github.com/devfile/library/pkg/devfile/parser/data" + "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/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/redhat-developer/odo/pkg/kclient" "github.com/redhat-developer/odo/pkg/labels" + "github.com/redhat-developer/odo/pkg/testingutil" + "github.com/redhat-developer/odo/pkg/util" "github.com/redhat-developer/odo/pkg/api" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func TestListAllClusterComponents(t *testing.T) { @@ -321,3 +333,117 @@ func TestGetRunningModes(t *testing.T) { }) } } + +func TestGatherName(t *testing.T) { + type devfileProvider func() (parser.DevfileObj, string, error) + fakeDevfileWithNameProvider := func(name string) devfileProvider { + return func() (parser.DevfileObj, string, error) { + dData, err := data.NewDevfileData(string(data.APISchemaVersion220)) + if err != nil { + return parser.DevfileObj{}, "", err + } + dData.SetMetadata(devfilepkg.DevfileMetadata{Name: name}) + return parser.DevfileObj{ + Ctx: devfileCtx.FakeContext(filesystem.NewFakeFs(), parser.OutputDevfileYamlPath), + Data: dData, + }, "", nil + } + } + + fs := filesystem.DefaultFs{} + //realDevfileWithNameProvider creates a real temporary directory and writes a devfile with the given name to it. + //It is the responsibility of the caller to remove the directory. + realDevfileWithNameProvider := func(name string) devfileProvider { + return func() (parser.DevfileObj, string, error) { + dir, err := fs.TempDir("", "Component_GatherName_") + if err != nil { + return parser.DevfileObj{}, dir, err + } + + originalDevfile := testingutil.GetTestDevfileObjFromFile("devfile.yaml") + originalDevfilePath := originalDevfile.Ctx.GetAbsPath() + + stat, err := os.Stat(originalDevfilePath) + if err != nil { + return parser.DevfileObj{}, dir, err + } + dPath := path.Join(dir, "devfile.yaml") + err = dfutil.CopyFile(originalDevfilePath, dPath, stat) + if err != nil { + return parser.DevfileObj{}, dir, err + } + + var d parser.DevfileObj + d, _, err = devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: dPath}) + if err != nil { + return parser.DevfileObj{}, dir, err + } + + err = d.SetMetadataName(name) + + return d, dir, err + } + } + + wantDevfileDirectoryName := func(d parser.DevfileObj) string { + return util.GetDNS1123Name(filepath.Base(filepath.Dir(d.Ctx.GetAbsPath()))) + } + + for _, tt := range []struct { + name string + devfileProviderFunc devfileProvider + wantErr bool + want func(parser.DevfileObj) string + }{ + { + name: "compliant name", + devfileProviderFunc: fakeDevfileWithNameProvider("my-component-name"), + want: func(parser.DevfileObj) string { return "my-component-name" }, + }, + { + name: "un-sanitized name", + devfileProviderFunc: fakeDevfileWithNameProvider("name with spaces"), + want: func(parser.DevfileObj) string { return "name-with-spaces" }, + }, + { + name: "all numeric name", + devfileProviderFunc: fakeDevfileWithNameProvider("123456789"), + // "x" prefix added by util.GetDNS1123Name + want: func(parser.DevfileObj) string { return "x123456789" }, + }, + { + name: "no name", + devfileProviderFunc: realDevfileWithNameProvider(""), + want: wantDevfileDirectoryName, + }, + { + name: "blank name", + devfileProviderFunc: realDevfileWithNameProvider(" "), + want: wantDevfileDirectoryName, + }, + } { + t.Run(tt.name, func(t *testing.T) { + d, dir, dErr := tt.devfileProviderFunc() + if dir != "" { + defer func(fs filesystem.DefaultFs, path string) { + if err := fs.RemoveAll(path); err != nil { + t.Logf("error while attempting to remove temporary directory %q: %v", path, err) + } + }(fs, dir) + } + if dErr != nil { + t.Errorf("error when building test Devfile object: %v", dErr) + return + } + + got, err := GatherName(d) + if (err != nil) != tt.wantErr { + t.Errorf("error = %v, wantErr %v", err, tt.wantErr) + } + want := tt.want(d) + if !reflect.DeepEqual(got, want) { + t.Errorf("GatherName() = %q, want = %q", got, want) + } + }) + } +} diff --git a/pkg/component/delete/delete.go b/pkg/component/delete/delete.go index f4539ab737c..6457124e082 100644 --- a/pkg/component/delete/delete.go +++ b/pkg/component/delete/delete.go @@ -95,7 +95,12 @@ func (do DeleteComponentClient) ListResourcesToDeleteFromDevfile(devfileObj pars if mode == odolabels.ComponentDevMode || mode == odolabels.ComponentAnyMode { // Inner Loop // Fetch the deployment of the devfile component - componentName := devfileObj.GetMetadataName() + var componentName string + componentName, err = component.GatherName(devfileObj) + if err != nil { + return isInnerLoopDeployed, resources, err + } + var deploymentName string deploymentName, err = util.NamespaceKubernetesObject(componentName, appName) if err != nil { @@ -155,7 +160,12 @@ func (do *DeleteComponentClient) ExecutePreStopEvents(devfileObj parser.DevfileO if !libdevfile.HasPreStopEvents(devfileObj) { return nil } - componentName := devfileObj.GetMetadataName() + + componentName, err := component.GatherName(devfileObj) + if err != nil { + return err + } + klog.V(4).Infof("Gathering information for component: %q", componentName) klog.V(3).Infof("Checking component status for %q", componentName) diff --git a/pkg/dev/dev.go b/pkg/dev/dev.go index bccf37a9306..283714e291b 100644 --- a/pkg/dev/dev.go +++ b/pkg/dev/dev.go @@ -14,8 +14,9 @@ import ( "github.com/devfile/library/pkg/devfile/parser" "k8s.io/klog/v2" + "github.com/redhat-developer/odo/pkg/component" "github.com/redhat-developer/odo/pkg/devfile/adapters" - "github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component" + k8sComponent "github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component" "github.com/redhat-developer/odo/pkg/watch" ) @@ -58,10 +59,16 @@ func (o *DevClient) Start( fs filesystem.Filesystem, ) (watch.ComponentStatus, error) { klog.V(4).Infoln("Creating new adapter") - adapter := component.NewKubernetesAdapter( + + componentName, err := component.GatherName(devfileObj) + if err != nil { + return watch.ComponentStatus{}, err + } + + adapter := k8sComponent.NewKubernetesAdapter( o.kubernetesClient, o.prefClient, o.portForwardClient, o.bindingClient, - component.AdapterContext{ - ComponentName: devfileObj.GetMetadataName(), + k8sComponent.AdapterContext{ + ComponentName: componentName, Context: path, AppName: "app", Devfile: devfileObj, @@ -118,10 +125,15 @@ func (o *DevClient) Watch( return err } + componentName, err := component.GatherName(devfileObj) + if err != nil { + return err + } + watchParameters := watch.WatchParameters{ DevfilePath: devfilePath, Path: path, - ComponentName: devfileObj.GetMetadataName(), + ComponentName: componentName, ApplicationName: "app", DevfileWatchHandler: h.RegenerateAdapterAndPush, EnvSpecificInfo: envSpecificInfo, diff --git a/pkg/devfile/adapters/kubernetes/component/commandhandler.go b/pkg/devfile/adapters/kubernetes/component/commandhandler.go index c4e4d71a17c..b93c732ffac 100644 --- a/pkg/devfile/adapters/kubernetes/component/commandhandler.go +++ b/pkg/devfile/adapters/kubernetes/component/commandhandler.go @@ -144,9 +144,14 @@ func ApplyKubernetes(mode, appName string, devfile parser.DevfileObj, kubernetes return err } + componentName, err := component.GatherName(devfile) + if err != nil { + return err + } + // Get the most common labels that's applicable to all resources being deployed. // Set the mode. Regardless of what Kubernetes resource we are deploying. - labels := odolabels.GetLabels(devfile.Data.GetMetadata().Name, appName, mode, false) + labels := odolabels.GetLabels(componentName, appName, mode, false) klog.V(4).Infof("Injecting labels: %+v into k8s artifact", labels) diff --git a/pkg/init/backend/alizer.go b/pkg/init/backend/alizer.go index 9c85d53cbf2..2633cc35d4c 100644 --- a/pkg/init/backend/alizer.go +++ b/pkg/init/backend/alizer.go @@ -53,7 +53,12 @@ func (o *AlizerBackend) SelectStarterProject(devfile parser.DevfileObj, flags ma } func (o *AlizerBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) (string, error) { - return devfile.GetMetadataName(), nil + // Get the absolute path to the directory from the Devfile context + path := devfile.Ctx.GetAbsPath() + if path == "" { + return "", fmt.Errorf("cannot determine the absolute path of the directory") + } + return alizer.DetectName(path) } func (o *AlizerBackend) PersonalizeDevfileConfig(devfile parser.DevfileObj) (parser.DevfileObj, error) { diff --git a/pkg/init/backend/interactive.go b/pkg/init/backend/interactive.go index 2648e5d8892..e53359e45a6 100644 --- a/pkg/init/backend/interactive.go +++ b/pkg/init/backend/interactive.go @@ -2,13 +2,16 @@ package backend import ( "fmt" + "path/filepath" "sort" "strconv" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/pkg/devfile/parser" parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common" + "k8s.io/klog" + "github.com/redhat-developer/odo/pkg/alizer" "github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/init/asker" "github.com/redhat-developer/odo/pkg/log" @@ -104,7 +107,34 @@ func (o *InteractiveBackend) SelectStarterProject(devfile parser.DevfileObj, fla } func (o *InteractiveBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) (string, error) { - return o.askerClient.AskName(fmt.Sprintf("my-%s-app", devfile.GetMetadataName())) + + // We will retrieve the name using alizer and then suggest it as the default name. + // 1. Check the pom.xml / package.json / etc. for the project name + // 2. If not, use the directory name instead + + // Get the absolute path to the directory from the Devfile context + path := devfile.Ctx.GetAbsPath() + if path == "" { + return "", fmt.Errorf("unable to get the absolute path to the directory: %q", path) + } + + // Pass in the BASE directory (not the file name of devfile.yaml) + // Convert path to base dir not file name + baseDir := filepath.Dir(path) + + // Detect the name + name, err := alizer.DetectName(baseDir) + if err != nil { + return "", fmt.Errorf("detecting name using alizer: %w", err) + } + + klog.V(4).Infof("Detected name via alizer: %q from path: %q", name, baseDir) + + if name == "" { + return "", fmt.Errorf("unable to detect the name") + } + + return o.askerClient.AskName(name) } func (o *InteractiveBackend) PersonalizeDevfileConfig(devfileobj parser.DevfileObj) (parser.DevfileObj, error) { diff --git a/pkg/init/backend/interactive_test.go b/pkg/init/backend/interactive_test.go index 7a85b4116e1..1636618d1f7 100644 --- a/pkg/init/backend/interactive_test.go +++ b/pkg/init/backend/interactive_test.go @@ -1,6 +1,8 @@ package backend import ( + "io/ioutil" + "os" "reflect" "testing" @@ -199,6 +201,16 @@ func TestInteractiveBackend_SelectStarterProject(t *testing.T) { } func TestInteractiveBackend_PersonalizeName(t *testing.T) { + + // Create a temporary file named devfile.yaml for alizer to "retrieve" + // We cannot use the "fake filesystem" in-memory to do this as it results + // in unresolved nil pointer panic's within Alizer's library. + tmpFile, err := ioutil.TempFile("", "devfile.yaml") + if err != nil { + t.Errorf("Error creating temporary file: %v", err) + } + defer os.Remove(tmpFile.Name()) + type fields struct { asker func(ctrl *gomock.Controller) asker.Asker registryClient registry.Client @@ -226,8 +238,9 @@ func TestInteractiveBackend_PersonalizeName(t *testing.T) { args: args{ devfile: func(fs filesystem.Filesystem) parser.DevfileObj { devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200)) + obj := parser.DevfileObj{ - Ctx: parsercontext.FakeContext(fs, "/tmp/devfile.yaml"), + Ctx: parsercontext.FakeContext(fs, tmpFile.Name()), Data: devfileData, } return obj diff --git a/pkg/init/init.go b/pkg/init/init.go index 5c2470805f5..8ba2cd0d239 100644 --- a/pkg/init/init.go +++ b/pkg/init/init.go @@ -228,6 +228,7 @@ func (o *InitClient) DownloadStarterProject(starter *v1alpha2.StarterProject, de // PersonalizeName calls PersonalizeName methods of the adequate backend func (o *InitClient) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) (string, error) { var backend backend.InitBackend + if len(flags) == 0 { backend = o.interactiveBackend } else { diff --git a/pkg/odo/cli/add/binding/binding.go b/pkg/odo/cli/add/binding/binding.go index 511e6d4db4f..ec086562253 100644 --- a/pkg/odo/cli/add/binding/binding.go +++ b/pkg/odo/cli/add/binding/binding.go @@ -12,6 +12,7 @@ import ( "github.com/redhat-developer/odo/pkg/binding/asker" "github.com/redhat-developer/odo/pkg/binding/backend" + "github.com/redhat-developer/odo/pkg/component" "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cmdline" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" @@ -123,7 +124,10 @@ func (o *AddBindingOptions) Run(_ context.Context) error { } componentName = workloadName } else { - componentName = o.EnvSpecificInfo.GetDevfileObj().GetMetadataName() + componentName, err = component.GatherName(o.EnvSpecificInfo.GetDevfileObj()) + if err != nil { + return err + } } bindingName, err := o.clientset.BindingClient.AskBindingName(serviceName, componentName, o.flags) diff --git a/pkg/odo/cli/delete/component/component.go b/pkg/odo/cli/delete/component/component.go index f392a1aeadd..830f748dd46 100644 --- a/pkg/odo/cli/delete/component/component.go +++ b/pkg/odo/cli/delete/component/component.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ktemplates "k8s.io/kubectl/pkg/util/templates" + "github.com/redhat-developer/odo/pkg/component" "github.com/redhat-developer/odo/pkg/labels" "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/odo/cli/ui" @@ -122,7 +123,12 @@ func (o *ComponentOptions) deleteNamedComponent() error { // deleteDevfileComponent deletes all the components defined by the devfile in the current directory func (o *ComponentOptions) deleteDevfileComponent() error { devfileObj := o.EnvSpecificInfo.GetDevfileObj() - componentName := devfileObj.GetMetadataName() + + componentName, err := component.GatherName(devfileObj) + if err != nil { + return err + } + namespace := o.GetProject() appName := "app" diff --git a/pkg/odo/cli/deploy/deploy.go b/pkg/odo/cli/deploy/deploy.go index 58b2e6fed5f..1b454a00793 100644 --- a/pkg/odo/cli/deploy/deploy.go +++ b/pkg/odo/cli/deploy/deploy.go @@ -146,7 +146,12 @@ func (o *DeployOptions) Validate() error { // Run contains the logic for the odo command func (o *DeployOptions) Run(ctx context.Context) error { devfileObj := o.EnvSpecificInfo.GetDevfileObj() - devfileName := devfileObj.GetMetadataName() + + devfileName, err := component.GatherName(devfileObj) + if err != nil { + return err + } + path := filepath.Dir(o.EnvSpecificInfo.GetDevfilePath()) appName := o.GetApplication() namespace := o.GetProject() @@ -160,7 +165,7 @@ func (o *DeployOptions) Run(ctx context.Context) error { "odo version: "+version.VERSION) // Run actual deploy command to be used - err := o.clientset.DeployClient.Deploy(o.clientset.FS, devfileObj, path, appName) + err = o.clientset.DeployClient.Deploy(o.clientset.FS, devfileObj, path, appName) if err == nil { log.Info("\nYour Devfile has been successfully deployed") diff --git a/pkg/odo/cli/describe/component.go b/pkg/odo/cli/describe/component.go index 412cee8b5f5..88a06872287 100644 --- a/pkg/odo/cli/describe/component.go +++ b/pkg/odo/cli/describe/component.go @@ -139,7 +139,13 @@ func (o *ComponentOptions) describeDevfileComponent() (result api.Component, dev if err != nil { return api.Component{}, nil, err } - runningIn, err := component.GetRunningModes(o.clientset.KubernetesClient, devfileObj.GetMetadataName()) + + componentName, err := component.GatherName(devfileObj) + if err != nil { + return api.Component{}, nil, err + } + + runningIn, err := component.GetRunningModes(o.clientset.KubernetesClient, componentName) if err != nil { if !errors.As(err, &component.NoComponentFoundError{}) { return api.Component{}, nil, err @@ -159,7 +165,11 @@ func (o *ComponentOptions) describeDevfileComponent() (result api.Component, dev func printHumanReadableOutput(cmp api.Component, devfileObj *parser.DevfileObj) error { if cmp.DevfileData != nil { - log.Describef("Name: ", cmp.DevfileData.Devfile.GetMetadata().Name) + componentName, err := component.GatherName(*devfileObj) + if err != nil { + return err + } + log.Describef("Name: ", componentName) log.Describef("Display Name: ", cmp.DevfileData.Devfile.GetMetadata().DisplayName) log.Describef("Project Type: ", cmp.DevfileData.Devfile.GetMetadata().ProjectType) log.Describef("Language: ", cmp.DevfileData.Devfile.GetMetadata().Language) diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index 90b60e54d49..1cdddced1a3 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -196,12 +196,16 @@ func (o *DevOptions) Validate() error { func (o *DevOptions) Run(ctx context.Context) (err error) { var ( - devFileObj = o.Context.EnvSpecificInfo.GetDevfileObj() - path = filepath.Dir(o.Context.EnvSpecificInfo.GetDevfilePath()) - devfileName = devFileObj.GetMetadataName() - namespace = o.GetProject() + devFileObj = o.Context.EnvSpecificInfo.GetDevfileObj() + path = filepath.Dir(o.Context.EnvSpecificInfo.GetDevfilePath()) + namespace = o.GetProject() ) + devfileName, err := component.GatherName(devFileObj) + if err != nil { + return err + } + defer func() { if err != nil { _ = o.clientset.WatchClient.CleanupDevResources(devFileObj, log.GetStdout()) @@ -248,7 +252,7 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { scontext.SetComponentType(ctx, component.GetComponentTypeFromDevfileMetadata(devFileObj.Data.GetMetadata())) scontext.SetLanguage(ctx, devFileObj.Data.GetMetadata().Language) scontext.SetProjectType(ctx, devFileObj.Data.GetMetadata().ProjectType) - scontext.SetDevfileName(ctx, devFileObj.GetMetadataName()) + scontext.SetDevfileName(ctx, devfileName) d := Handler{ clientset: *o.clientset, diff --git a/pkg/odo/cli/list/list.go b/pkg/odo/cli/list/list.go index 5d0c6703806..ba3c1d23428 100644 --- a/pkg/odo/cli/list/list.go +++ b/pkg/odo/cli/list/list.go @@ -100,8 +100,12 @@ func (lo *ListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err err } // Create a local component from the parse devfile + componentName, err := component.GatherName(devObj) + if err != nil { + return err + } localComponent := api.ComponentAbstract{ - Name: devObj.Data.GetMetadata().Name, + Name: componentName, ManagedBy: "", RunningIn: []api.RunningMode{}, Type: component.GetComponentTypeFromDevfileMetadata(devObj.Data.GetMetadata()), diff --git a/pkg/odo/cli/logs/logs.go b/pkg/odo/cli/logs/logs.go index 6779c5173fb..43bb4e4d708 100644 --- a/pkg/odo/cli/logs/logs.go +++ b/pkg/odo/cli/logs/logs.go @@ -5,8 +5,6 @@ import ( "context" "errors" "fmt" - "github.com/fatih/color" - odolabels "github.com/redhat-developer/odo/pkg/labels" "io" "os" "strconv" @@ -14,16 +12,22 @@ import ( "sync" "sync/atomic" + "github.com/fatih/color" + + "github.com/redhat-developer/odo/pkg/component" + odolabels "github.com/redhat-developer/odo/pkg/labels" + "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/devfile/location" odoutil "github.com/redhat-developer/odo/pkg/odo/util" + "github.com/spf13/cobra" + ktemplates "k8s.io/kubectl/pkg/util/templates" + "github.com/redhat-developer/odo/pkg/odo/cmdline" "github.com/redhat-developer/odo/pkg/odo/genericclioptions" "github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset" - "github.com/spf13/cobra" - ktemplates "k8s.io/kubectl/pkg/util/templates" ) const RecommendedCommandName = "logs" @@ -88,7 +92,10 @@ func (o *LogsOptions) Complete(cmdline cmdline.Cmdline, _ []string) error { return fmt.Errorf("unable to create context: %v", err) } - o.componentName = o.Context.EnvSpecificInfo.GetDevfileObj().GetMetadataName() + o.componentName, err = component.GatherName(o.Context.EnvSpecificInfo.GetDevfileObj()) + if err != nil { + return err + } o.clientset.KubernetesClient.SetNamespace(o.Context.GetProject()) diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index c9228b15582..b7444ad9f6d 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -14,7 +14,6 @@ import ( "github.com/devfile/registry-support/registry-library/library" "github.com/redhat-developer/odo/pkg/api" - "github.com/redhat-developer/odo/pkg/component" "github.com/redhat-developer/odo/pkg/devfile" "github.com/redhat-developer/odo/pkg/devfile/location" "github.com/redhat-developer/odo/pkg/log" @@ -51,7 +50,7 @@ func (o RegistryClient) DownloadFileInMemory(params dfutil.HTTPRequestParams) ([ // DownloadStarterProject downloads a starter project referenced in devfile // This will first remove the content of the contextDir func (o RegistryClient) DownloadStarterProject(starterProject *devfilev1.StarterProject, decryptedToken string, contextDir string, verbose bool) error { - return component.DownloadStarterProject(starterProject, decryptedToken, contextDir, verbose) + return DownloadStarterProject(starterProject, decryptedToken, contextDir, verbose) } // GetDevfileRegistries gets devfile registries from preference file, diff --git a/pkg/component/starter_project.go b/pkg/registry/starter_project.go similarity index 99% rename from pkg/component/starter_project.go rename to pkg/registry/starter_project.go index 7d06f96cb98..2c189afabd4 100644 --- a/pkg/component/starter_project.go +++ b/pkg/registry/starter_project.go @@ -1,4 +1,4 @@ -package component +package registry import ( "errors" diff --git a/tests/helper/helper_filesystem.go b/tests/helper/helper_filesystem.go index 09fb34df2ac..b4b31894fd0 100644 --- a/tests/helper/helper_filesystem.go +++ b/tests/helper/helper_filesystem.go @@ -9,7 +9,9 @@ import ( "strings" "time" + "github.com/devfile/library/pkg/devfile/parser" dfutil "github.com/devfile/library/pkg/util" + "k8s.io/utils/pointer" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -273,3 +275,35 @@ func CreateSimpleFile(context, filePrefix, fileExtension string) (string, string return FilePath, string(content) } + +// DevfileUpdater is a helper type that can mutate a Devfile object. +// It is intended to be used in conjunction with the UpdateDevfileContent function. +type DevfileUpdater func(*parser.DevfileObj) error + +// DevfileMetadataNameRemover removes the 'metadata.name' field from the given Devfile +var DevfileMetadataNameRemover = func(d *parser.DevfileObj) error { + return d.SetMetadataName("") +} + +// UpdateDevfileContent parses the Devfile at the given path, then updates its content using the given handlers, and writes the updated Devfile to the given path. +// +// The handlers are invoked in the order they are provided. +// +// No operation is performed if no handler function is specified. +// +// See DevfileMetadataNameRemover for an example of handler function that can operate on the Devfile content. +func UpdateDevfileContent(path string, handlers []DevfileUpdater) { + if len(handlers) == 0 { + //Nothing to do => skip + return + } + + d, err := parser.ParseDevfile(parser.ParserArgs{Path: path, FlattenedDevfile: pointer.BoolPtr(false)}) + Expect(err).NotTo(HaveOccurred()) + for _, h := range handlers { + err = h(&d) + Expect(err).NotTo(HaveOccurred()) + } + err = d.WriteYamlDevfile() + Expect(err).NotTo(HaveOccurred()) +} diff --git a/tests/integration/cmd_dev_debug_test.go b/tests/integration/cmd_dev_debug_test.go index a808ed9220c..02b69508e01 100644 --- a/tests/integration/cmd_dev_debug_test.go +++ b/tests/integration/cmd_dev_debug_test.go @@ -56,72 +56,102 @@ var _ = Describe("odo dev debug command tests", func() { }) }) - When("a composite command is used as debug command", func() { - const devfileCmpName = "nodejs" - var session helper.DevSession - var stdout []byte - var stderr []byte - var ports map[string]string - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeRunAndDebug.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - var err error - session, stdout, stderr, ports, err = helper.StartDevMode(nil, "--debug") - Expect(err).ToNot(HaveOccurred()) - }) + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "nodejs" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("a composite command is used as debug command - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + var stdout []byte + var stderr []byte + var ports map[string]string + BeforeEach(func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeRunAndDebug.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, stdout, stderr, ports, err = helper.StartDevMode(nil, "--debug") + Expect(err).ToNot(HaveOccurred()) + }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should run successfully", func() { - By("verifying from the output that all commands have been executed", func() { - helper.MatchAllInOutput(string(stdout), []string{ - "Building your application in container on cluster", - "Executing the application (command: mkdir)", - "Executing the application (command: echo)", - "Executing the application (command: install)", - "Executing the application (command: start-debug)", + It("should run successfully", func() { + By("verifying from the output that all commands have been executed", func() { + helper.MatchAllInOutput(string(stdout), []string{ + "Building your application in container on cluster", + "Executing the application (command: mkdir)", + "Executing the application (command: echo)", + "Executing the application (command: install)", + "Executing the application (command: start-debug)", + }) }) - }) - By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { - helper.MatchAllInOutput(string(stderr), []string{ - "Devfile command \"echo\" exited with an error status", - "intentional-error-message", + By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { + helper.MatchAllInOutput(string(stderr), []string{ + "Devfile command \"echo\" exited with an error status", + "intentional-error-message", + }) }) - }) - By("building the application only once", func() { - // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. - // The bracket allows to match the last occurrence with the command execution timing information. - Expect(strings.Count(string(stdout), "Building your application in container on cluster (command: install) [")). - To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") - }) + By("building the application only once", func() { + // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. + // The bracket allows to match the last occurrence with the command execution timing information. + Expect(strings.Count(string(stdout), "Building your application in container on cluster (command: install) [")). + To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") + }) - By("verifying that the command did run successfully", func() { - // Verify the command executed successfully - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime", - commonVar.Project, - []string{"stat", "/projects/testfolder"}, - func(cmdOp string, err error) bool { - return err == nil - }, - ) - Expect(res).To(BeTrue()) - }) + By("verifying that the command did run successfully", func() { + // Verify the command executed successfully + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime", + commonVar.Project, + []string{"stat", "/projects/testfolder"}, + func(cmdOp string, err error) bool { + return err == nil + }, + ) + Expect(res).To(BeTrue()) + }) - By("expecting a ws connection when tried to connect on default debug port locally", func() { - // 400 response expected because the endpoint expects a websocket request and we are doing a HTTP GET - // We are just using this to validate if nodejs agent is listening on the other side - helper.HttpWaitForWithStatus("http://"+ports["5858"], "WebSockets request was expected", 12, 5, 400) + By("expecting a ws connection when tried to connect on default debug port locally", func() { + // 400 response expected because the endpoint expects a websocket request and we are doing a HTTP GET + // We are just using this to validate if nodejs agent is listening on the other side + helper.HttpWaitForWithStatus("http://"+ports["5858"], "WebSockets request was expected", 12, 5, 400) + }) }) }) - }) + } + When("a composite apply command is used as debug command", func() { deploymentName := "my-component" var session helper.DevSession @@ -189,79 +219,105 @@ var _ = Describe("odo dev debug command tests", func() { }) }) - When("running build and debug commands as composite in different containers and a shared volume", func() { - const devfileCmpName = "nodejs" - var session helper.DevSession - var stdout []byte - var stderr []byte - var ports map[string]string - BeforeEach(func() { - helper.CopyExampleDevFile( - filepath.Join("source", "devfiles", "nodejs", "devfileCompositeBuildRunDebugInMultiContainersAndSharedVolume.yaml"), - filepath.Join(commonVar.Context, "devfile.yaml")) - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - var err error - session, stdout, stderr, ports, err = helper.StartDevMode(nil, "--debug") - Expect(err).ToNot(HaveOccurred()) - }) + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { return "nodejs" }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("running build and debug commands as composite in different containers and a shared volume - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + var stdout []byte + var stderr []byte + var ports map[string]string + BeforeEach(func() { + helper.CopyExampleDevFile( + filepath.Join("source", "devfiles", "nodejs", "devfileCompositeBuildRunDebugInMultiContainersAndSharedVolume.yaml"), + filepath.Join(commonVar.Context, "devfile.yaml")) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, stdout, stderr, ports, err = helper.StartDevMode(nil, "--debug") + Expect(err).ToNot(HaveOccurred()) + }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should run successfully", func() { - By("verifying from the output that all commands have been executed", func() { - helper.MatchAllInOutput(string(stdout), []string{ - "Building your application in container on cluster (command: mkdir)", - "Building your application in container on cluster (command: sleep-cmd-build)", - "Building your application in container on cluster (command: build-cmd)", - "Executing the application (command: sleep-cmd-run)", - "Executing the application (command: echo-with-error)", - "Executing the application (command: check-build-result)", - "Executing the application (command: start-debug)", + It("should run successfully", func() { + By("verifying from the output that all commands have been executed", func() { + helper.MatchAllInOutput(string(stdout), []string{ + "Building your application in container on cluster (command: mkdir)", + "Building your application in container on cluster (command: sleep-cmd-build)", + "Building your application in container on cluster (command: build-cmd)", + "Executing the application (command: sleep-cmd-run)", + "Executing the application (command: echo-with-error)", + "Executing the application (command: check-build-result)", + "Executing the application (command: start-debug)", + }) }) - }) - By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { - helper.MatchAllInOutput(string(stderr), []string{ - "Devfile command \"echo-with-error\" exited with an error status", - "intentional-error-message", + By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { + helper.MatchAllInOutput(string(stderr), []string{ + "Devfile command \"echo-with-error\" exited with an error status", + "intentional-error-message", + }) }) - }) - By("building the application only once per exec command in the build command", func() { - // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. - // The bracket allows to match the last occurrence with the command execution timing information. - out := string(stdout) - for _, cmd := range []string{"mkdir", "sleep-cmd-build", "build-cmd"} { - Expect(strings.Count(out, fmt.Sprintf("Building your application in container on cluster (command: %s) [", cmd))). - To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") - } - }) + By("building the application only once per exec command in the build command", func() { + // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. + // The bracket allows to match the last occurrence with the command execution timing information. + out := string(stdout) + for _, cmd := range []string{"mkdir", "sleep-cmd-build", "build-cmd"} { + Expect(strings.Count(out, fmt.Sprintf("Building your application in container on cluster (command: %s) [", cmd))). + To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") + } + }) - By("verifying that the command did run successfully", func() { - // Verify the command executed successfully - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime", - commonVar.Project, - []string{"stat", "/projects/testfolder"}, - func(cmdOp string, err error) bool { - return err == nil - }, - ) - Expect(res).To(BeTrue()) - }) + By("verifying that the command did run successfully", func() { + // Verify the command executed successfully + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime", + commonVar.Project, + []string{"stat", "/projects/testfolder"}, + func(cmdOp string, err error) bool { + return err == nil + }, + ) + Expect(res).To(BeTrue()) + }) - By("expecting a ws connection when tried to connect on default debug port locally", func() { - // 400 response expected because the endpoint expects a websocket request and we are doing a HTTP GET - // We are just using this to validate if nodejs agent is listening on the other side - helper.HttpWaitForWithStatus("http://"+ports["5858"], "WebSockets request was expected", 12, 5, 400) + By("expecting a ws connection when tried to connect on default debug port locally", func() { + // 400 response expected because the endpoint expects a websocket request and we are doing a HTTP GET + // We are just using this to validate if nodejs agent is listening on the other side + helper.HttpWaitForWithStatus("http://"+ports["5858"], "WebSockets request was expected", 12, 5, 400) + }) }) }) - }) + } When("a component without debug command is bootstrapped", func() { BeforeEach(func() { diff --git a/tests/integration/cmd_dev_test.go b/tests/integration/cmd_dev_test.go index c8580cda59c..f42461c0b22 100644 --- a/tests/integration/cmd_dev_test.go +++ b/tests/integration/cmd_dev_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "os" + "path" "path/filepath" "regexp" "sort" @@ -605,227 +606,202 @@ var _ = Describe("odo dev command tests", func() { }) }) - When("Devfile 2.1.0 is used", func() { - // from devfile - devfileCmpName := "nodejs" - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-variables.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - }) - - When("doing odo dev", func() { - var session helper.DevSession + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "nodejs" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("Devfile 2.1.0 is used - "+devfileHandlerCtx.name, func() { + var devfileCmpName string BeforeEach(func() { - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-variables.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } }) - It("should check if the env variable has a correct value", func() { - envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) - // check if the env variable has a correct value. This value was substituted from in devfile from variable - Expect(envVars["FOO"]).To(Equal("bar")) - }) - }) + When("doing odo dev", func() { + var session helper.DevSession + BeforeEach(func() { + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - When("doing odo dev with --var flag", func() { - var session helper.DevSession - BeforeEach(func() { - var err error - session, _, _, _, err = helper.StartDevMode(nil, "--var", "VALUE_TEST=baz") - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() + It("should check if the env variable has a correct value", func() { + envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) + // check if the env variable has a correct value. This value was substituted from in devfile from variable + Expect(envVars["FOO"]).To(Equal("bar")) + }) }) - It("should check if the env variable has a correct value", func() { - envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) - // check if the env variable has a correct value. This value was substituted from in devfile from variable - Expect(envVars["FOO"]).To(Equal("baz")) - }) - }) + When("doing odo dev with --var flag", func() { + var session helper.DevSession + BeforeEach(func() { + var err error + session, _, _, _, err = helper.StartDevMode(nil, "--var", "VALUE_TEST=baz") + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - When("doing odo dev with --var-file flag", func() { - var session helper.DevSession - varfilename := "vars.txt" - BeforeEach(func() { - var err error - err = helper.CreateFileWithContent(varfilename, "VALUE_TEST=baz") - Expect(err).ToNot(HaveOccurred()) - session, _, _, _, err = helper.StartDevMode(nil, "--var-file", "vars.txt") - Expect(err).ToNot(HaveOccurred()) + It("should check if the env variable has a correct value", func() { + envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) + // check if the env variable has a correct value. This value was substituted from in devfile from variable + Expect(envVars["FOO"]).To(Equal("baz")) + }) }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - helper.DeleteFile(varfilename) + + When("doing odo dev with --var-file flag", func() { + var session helper.DevSession + varfilename := "vars.txt" + BeforeEach(func() { + var err error + err = helper.CreateFileWithContent(varfilename, "VALUE_TEST=baz") + Expect(err).ToNot(HaveOccurred()) + session, _, _, _, err = helper.StartDevMode(nil, "--var-file", "vars.txt") + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + helper.DeleteFile(varfilename) + }) + + It("should check if the env variable has a correct value", func() { + envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) + // check if the env variable has a correct value. This value was substituted from in devfile from variable + Expect(envVars["FOO"]).To(Equal("baz")) + }) }) - It("should check if the env variable has a correct value", func() { - envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) - // check if the env variable has a correct value. This value was substituted from in devfile from variable - Expect(envVars["FOO"]).To(Equal("baz")) + When("doing odo dev with --var-file flag and setting value in env", func() { + var session helper.DevSession + varfilename := "vars.txt" + BeforeEach(func() { + var err error + _ = os.Setenv("VALUE_TEST", "baz") + err = helper.CreateFileWithContent(varfilename, "VALUE_TEST") + Expect(err).ToNot(HaveOccurred()) + session, _, _, _, err = helper.StartDevMode(nil, "--var-file", "vars.txt") + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + helper.DeleteFile(varfilename) + _ = os.Unsetenv("VALUE_TEST") + }) + + It("should check if the env variable has a correct value", func() { + envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) + // check if the env variable has a correct value. This value was substituted from in devfile from variable + Expect(envVars["FOO"]).To(Equal("baz")) + }) }) }) - When("doing odo dev with --var-file flag and setting value in env", func() { - var session helper.DevSession - varfilename := "vars.txt" + When("running odo dev and single env var is set - "+devfileHandlerCtx.name, func() { + var devfileCmpName string BeforeEach(func() { - var err error - _ = os.Setenv("VALUE_TEST", "baz") - err = helper.CreateFileWithContent(varfilename, "VALUE_TEST") - Expect(err).ToNot(HaveOccurred()) - session, _, _, _, err = helper.StartDevMode(nil, "--var-file", "vars.txt") - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - helper.DeleteFile(varfilename) - _ = os.Unsetenv("VALUE_TEST") + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-single-env.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } }) - It("should check if the env variable has a correct value", func() { - envVars := commonVar.CliRunner.GetEnvsDevFileDeployment(devfileCmpName, "app", commonVar.Project) - // check if the env variable has a correct value. This value was substituted from in devfile from variable - Expect(envVars["FOO"]).To(Equal("baz")) + It("should be able to exec command", func() { + err := helper.RunDevMode(nil, nil, func(session *gexec.Session, out, err []byte, ports map[string]string) { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.MatchAllInOutput(output, []string{"test_env_variable", "test_build_env_variable"}) + }) + Expect(err).ToNot(HaveOccurred()) }) }) - }) - - When("running odo dev and single env var is set", func() { - devfileCmpName := "nodejs" - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-single-env.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - }) - It("should be able to exec command", func() { - err := helper.RunDevMode(nil, nil, func(session *gexec.Session, out, err []byte, ports map[string]string) { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.MatchAllInOutput(output, []string{"test_env_variable", "test_build_env_variable"}) + When("running odo dev and multiple env variables are set - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-multiple-envs.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("running odo dev and multiple env variables are set", func() { - devfileCmpName := "nodejs" - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-multiple-envs.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - }) - - It("should be able to exec command", func() { - err := helper.RunDevMode(nil, nil, func(session *gexec.Session, out, err []byte, ports map[string]string) { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.MatchAllInOutput(output, []string{"test_build_env_variable1", "test_build_env_variable2", "test_env_variable1", "test_env_variable2"}) + It("should be able to exec command", func() { + err := helper.RunDevMode(nil, nil, func(session *gexec.Session, out, err []byte, ports map[string]string) { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.MatchAllInOutput(output, []string{"test_build_env_variable1", "test_build_env_variable2", "test_env_variable1", "test_env_variable2"}) + }) + Expect(err).ToNot(HaveOccurred()) }) - Expect(err).ToNot(HaveOccurred()) }) - }) - When("doing odo dev and there is a env variable with spaces", func() { - devfileCmpName := "nodejs" - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-env-with-space.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - }) - - It("should be able to exec command", func() { - err := helper.RunDevMode(nil, nil, func(session *gexec.Session, out, err []byte, ports map[string]string) { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.MatchAllInOutput(output, []string{"build env variable with space", "env with space"}) + When("doing odo dev and there is a env variable with spaces - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-command-env-with-space.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - When("creating local files and dir and running odo dev", func() { - var newDirPath, newFilePath, stdOut, podName string - var session helper.DevSession - // from devfile - devfileCmpName := "nodejs" - BeforeEach(func() { - newFilePath = filepath.Join(commonVar.Context, "foobar.txt") - newDirPath = filepath.Join(commonVar.Context, "testdir") - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - // Create a new file that we plan on deleting later... - if err := helper.CreateFileWithContent(newFilePath, "hello world"); err != nil { - fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) - } - // Create a new directory - helper.MakeDir(newDirPath) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) - - It("should correctly propagate changes to the container", func() { - - // Check to see if it's been pushed (foobar.txt abd directory testdir) - podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.MatchAllInOutput(stdOut, []string{"foobar.txt", "testdir"}) - }) - - When("deleting local files and dir and waiting for sync", func() { - BeforeEach(func() { - // Now we delete the file and dir and push - helper.DeleteDir(newFilePath) - helper.DeleteDir(newDirPath) - _, _, _, err := session.WaitSync() + It("should be able to exec command", func() { + err := helper.RunDevMode(nil, nil, func(session *gexec.Session, out, err []byte, ports map[string]string) { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.MatchAllInOutput(output, []string{"build env variable with space", "env with space"}) + }) Expect(err).ToNot(HaveOccurred()) }) - It("should not list deleted dir and file in container", func() { - podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - // Then check to see if it's truly been deleted - stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt", "testdir"}) - }) }) - }) - When("Starting a PostgreSQL service", func() { - BeforeEach(func() { - if helper.IsKubernetesCluster() { - Skip("Operators have not been setup on Kubernetes cluster yet. Remove this once the issue has been fixed.") - } - // Ensure that the operators are installed - commonVar.CliRunner.EnsureOperatorIsInstalled("service-binding-operator") - commonVar.CliRunner.EnsureOperatorIsInstalled("cloud-native-postgresql") - Eventually(func() string { - out, _ := commonVar.CliRunner.GetBindableKinds() - return out - }, 120, 3).Should(ContainSubstring("Cluster")) - addBindableKind := commonVar.CliRunner.Run("apply", "-f", helper.GetExamplePath("manifests", "bindablekind-instance.yaml")) - Expect(addBindableKind.ExitCode()).To(BeEquivalentTo(0)) - }) - - When("creating local files and dir and running odo dev", func() { + When("creating local files and dir and running odo dev - "+devfileHandlerCtx.name, func() { var newDirPath, newFilePath, stdOut, podName string var session helper.DevSession - // from devfile - devfileCmpName := "my-nodejs-app" + var devfileCmpName string BeforeEach(func() { newFilePath = filepath.Join(commonVar.Context, "foobar.txt") newDirPath = filepath.Join(commonVar.Context, "testdir") - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-service-binding-files.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } // Create a new file that we plan on deleting later... if err := helper.CreateFileWithContent(newFilePath, "hello world"); err != nil { fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) @@ -867,332 +843,530 @@ var _ = Describe("odo dev command tests", func() { }) }) }) - }) + } - When("adding local files to gitignore and running odo dev", func() { - var gitignorePath, newDirPath, newFilePath1, newFilePath2, newFilePath3, stdOut, podName string - var session helper.DevSession - // from devfile - devfileCmpName := "nodejs" + When("Starting a PostgreSQL service", func() { BeforeEach(func() { - gitignorePath = filepath.Join(commonVar.Context, ".gitignore") - newFilePath1 = filepath.Join(commonVar.Context, "foobar.txt") - newDirPath = filepath.Join(commonVar.Context, "testdir") - newFilePath2 = filepath.Join(newDirPath, "foobar.txt") - newFilePath3 = filepath.Join(newDirPath, "baz.txt") - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - if err := helper.CreateFileWithContent(newFilePath1, "hello world"); err != nil { - fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) - } - // Create a new directory - helper.MakeDir(newDirPath) - if err := helper.CreateFileWithContent(newFilePath2, "hello world"); err != nil { - fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) - } - if err := helper.CreateFileWithContent(newFilePath3, "hello world"); err != nil { - fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) - } - if err := helper.CreateFileWithContent(gitignorePath, "foobar.txt"); err != nil { - fmt.Printf("the .gitignore file was not created, reason %v", err.Error()) + if helper.IsKubernetesCluster() { + Skip("Operators have not been setup on Kubernetes cluster yet. Remove this once the issue has been fixed.") } - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() + // Ensure that the operators are installed + commonVar.CliRunner.EnsureOperatorIsInstalled("service-binding-operator") + commonVar.CliRunner.EnsureOperatorIsInstalled("cloud-native-postgresql") + Eventually(func() string { + out, _ := commonVar.CliRunner.GetBindableKinds() + return out + }, 120, 3).Should(ContainSubstring("Cluster")) + addBindableKind := commonVar.CliRunner.Run("apply", "-f", helper.GetExamplePath("manifests", "bindablekind-instance.yaml")) + Expect(addBindableKind.ExitCode()).To(BeEquivalentTo(0)) }) - checkSyncedFiles := func(podName string) { - stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.MatchAllInOutput(stdOut, []string{"testdir"}) - helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt"}) - stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects/testdir") - helper.MatchAllInOutput(stdOut, []string{"baz.txt"}) - helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt"}) - } + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "my-nodejs-app" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("creating local files and dir and running odo dev - "+devfileHandlerCtx.name, func() { + var newDirPath, newFilePath, stdOut, podName string + var session helper.DevSession + var devfileCmpName string + BeforeEach(func() { + newFilePath = filepath.Join(commonVar.Context, "foobar.txt") + newDirPath = filepath.Join(commonVar.Context, "testdir") + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-service-binding-files.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + // Create a new file that we plan on deleting later... + if err := helper.CreateFileWithContent(newFilePath, "hello world"); err != nil { + fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) + } + // Create a new directory + helper.MakeDir(newDirPath) + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) - It("should not sync ignored files to the container", func() { - podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - checkSyncedFiles(podName) - }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) + + It("should correctly propagate changes to the container", func() { + + // Check to see if it's been pushed (foobar.txt abd directory testdir) + podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + + stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.MatchAllInOutput(stdOut, []string{"foobar.txt", "testdir"}) + }) - When("modifying /testdir/baz.txt file", func() { + When("deleting local files and dir and waiting for sync", func() { + BeforeEach(func() { + // Now we delete the file and dir and push + helper.DeleteDir(newFilePath) + helper.DeleteDir(newDirPath) + _, _, _, err := session.WaitSync() + Expect(err).ToNot(HaveOccurred()) + }) + It("should not list deleted dir and file in container", func() { + podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + // Then check to see if it's truly been deleted + stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt", "testdir"}) + }) + }) + }) + } + }) + + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { return "nodejs" }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("adding local files to gitignore and running odo dev", func() { + var gitignorePath, newDirPath, newFilePath1, newFilePath2, newFilePath3, stdOut, podName string + var session helper.DevSession + var devfileCmpName string BeforeEach(func() { - helper.ReplaceString(newFilePath3, "hello world", "hello world!!!") + gitignorePath = filepath.Join(commonVar.Context, ".gitignore") + newFilePath1 = filepath.Join(commonVar.Context, "foobar.txt") + newDirPath = filepath.Join(commonVar.Context, "testdir") + newFilePath2 = filepath.Join(newDirPath, "foobar.txt") + newFilePath3 = filepath.Join(newDirPath, "baz.txt") + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + if err := helper.CreateFileWithContent(newFilePath1, "hello world"); err != nil { + fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) + } + // Create a new directory + helper.MakeDir(newDirPath) + if err := helper.CreateFileWithContent(newFilePath2, "hello world"); err != nil { + fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) + } + if err := helper.CreateFileWithContent(newFilePath3, "hello world"); err != nil { + fmt.Printf("the foobar.txt file was not created, reason %v", err.Error()) + } + if err := helper.CreateFileWithContent(gitignorePath, "foobar.txt"); err != nil { + fmt.Printf("the .gitignore file was not created, reason %v", err.Error()) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() }) - It("should synchronize it only", func() { - _, _, _, _ = session.WaitSync() + checkSyncedFiles := func(podName string) { + stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.MatchAllInOutput(stdOut, []string{"testdir"}) + helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt"}) + stdOut = commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects/testdir") + helper.MatchAllInOutput(stdOut, []string{"baz.txt"}) + helper.DontMatchAllInOutput(stdOut, []string{"foobar.txt"}) + } + + It("should not sync ignored files to the container", func() { podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) checkSyncedFiles(podName) }) - }) - When("modifying /foobar.txt file", func() { - BeforeEach(func() { - helper.ReplaceString(newFilePath1, "hello world", "hello world!!!") - }) + When("modifying /testdir/baz.txt file", func() { + BeforeEach(func() { + helper.ReplaceString(newFilePath3, "hello world", "hello world!!!") + }) - It("should not synchronize it", func() { - session.CheckNotSynced(10 * time.Second) + It("should synchronize it only", func() { + _, _, _, _ = session.WaitSync() + podName = commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + checkSyncedFiles(podName) + }) }) - }) - When("modifying /testdir/foobar.txt file", func() { - BeforeEach(func() { - helper.ReplaceString(newFilePath2, "hello world", "hello world!!!") + When("modifying /foobar.txt file", func() { + BeforeEach(func() { + helper.ReplaceString(newFilePath1, "hello world", "hello world!!!") + }) + + It("should not synchronize it", func() { + session.CheckNotSynced(10 * time.Second) + }) }) - It("should not synchronize it", func() { - session.CheckNotSynced(10 * time.Second) + When("modifying /testdir/foobar.txt file", func() { + BeforeEach(func() { + helper.ReplaceString(newFilePath2, "hello world", "hello world!!!") + }) + + It("should not synchronize it", func() { + session.CheckNotSynced(10 * time.Second) + }) }) }) - }) - When("devfile has sourcemappings and running odo dev", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileSourceMapping.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) + When("devfile has sourcemappings and running odo dev - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileSourceMapping.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should sync files to the correct location", func() { - // Verify source code was synced to /test instead of /projects - var statErr error - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime", - commonVar.Project, - []string{"stat", "/test/server.js"}, - func(cmdOp string, err error) bool { - statErr = err - return err == nil - }, - ) - Expect(statErr).ToNot(HaveOccurred()) + It("should sync files to the correct location", func() { + // Verify source code was synced to /test instead of /projects + var statErr error + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime", + commonVar.Project, + []string{"stat", "/test/server.js"}, + func(cmdOp string, err error) bool { + statErr = err + return err == nil + }, + ) + Expect(statErr).ToNot(HaveOccurred()) + }) }) - }) - When("project and clonePath is present in devfile and running odo dev", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - // devfile with clonePath set in project field - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + When("project and clonePath is present in devfile and running odo dev - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + // devfile with clonePath set in project field + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should sync to the correct dir in container", func() { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - // source code is synced to $PROJECTS_ROOT/clonePath - // $PROJECTS_ROOT is /projects by default, if sourceMapping is set it is same as sourceMapping - // for devfile-with-projects.yaml, sourceMapping is apps and clonePath is webapp - // so source code would be synced to /apps/webapp - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/apps/webapp") - helper.MatchAllInOutput(output, []string{"package.json"}) + It("should sync to the correct dir in container", func() { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + // source code is synced to $PROJECTS_ROOT/clonePath + // $PROJECTS_ROOT is /projects by default, if sourceMapping is set it is same as sourceMapping + // for devfile-with-projects.yaml, sourceMapping is apps and clonePath is webapp + // so source code would be synced to /apps/webapp + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/apps/webapp") + helper.MatchAllInOutput(output, []string{"package.json"}) - // Verify the sync env variables are correct - helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/apps/webapp", "/apps", commonVar.CliRunner) + // Verify the sync env variables are correct + helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/apps/webapp", "/apps", commonVar.CliRunner) + }) }) - }) - When("devfile project field is present and running odo dev", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + When("devfile project field is present and running odo dev - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(path.Join(commonVar.Context, "devfile.yaml")) + } - // reset clonePath and change the workdir accordingly, it should sync to project name - helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), "clonePath: webapp/", "# clonePath: webapp/") - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + // reset clonePath and change the workdir accordingly, it should sync to project name + helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), "clonePath: webapp/", "# clonePath: webapp/") + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should sync to the correct dir in container", func() { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/apps/nodeshift") - helper.MatchAllInOutput(output, []string{"package.json"}) + It("should sync to the correct dir in container", func() { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/apps/nodeshift") + helper.MatchAllInOutput(output, []string{"package.json"}) - // Verify the sync env variables are correct - helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/apps/nodeshift", "/apps", commonVar.CliRunner) + // Verify the sync env variables are correct + helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/apps/nodeshift", "/apps", commonVar.CliRunner) + }) }) - }) - When("multiple project are present", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-multiple-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + When("multiple project are present - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-multiple-projects.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - It("should sync to the correct dir in container", func() { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - // for devfile-with-multiple-projects.yaml source mapping is not set so $PROJECTS_ROOT is /projects - // multiple projects, so source code would sync to the first project /projects/webapp - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects/webapp") - helper.MatchAllInOutput(output, []string{"package.json"}) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } - // Verify the sync env variables are correct - helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/projects/webapp", "/projects", commonVar.CliRunner) - }) - }) + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - When("no project is present", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + It("should sync to the correct dir in container", func() { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + // for devfile-with-multiple-projects.yaml source mapping is not set so $PROJECTS_ROOT is /projects + // multiple projects, so source code would sync to the first project /projects/webapp + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects/webapp") + helper.MatchAllInOutput(output, []string{"package.json"}) + + // Verify the sync env variables are correct + helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/projects/webapp", "/projects", commonVar.CliRunner) + }) + }) + } + + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { return "nodejs" }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("no project is present - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should sync to the correct dir in container", func() { + It("should sync to the correct dir in container", func() { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") - helper.MatchAllInOutput(output, []string{"package.json"}) + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + output := commonVar.CliRunner.ExecListDir(podName, commonVar.Project, "/projects") + helper.MatchAllInOutput(output, []string{"package.json"}) - // Verify the sync env variables are correct - helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/projects", "/projects", commonVar.CliRunner) + // Verify the sync env variables are correct + helper.VerifyContainerSyncEnv(podName, "runtime", commonVar.Project, "/projects", "/projects", commonVar.CliRunner) + }) }) - }) - When("running odo dev with devfile contain volume", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-volumes.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + When("running odo dev with devfile contain volume - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-volumes.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should create pvc and reuse if it shares the same devfile volume name", func() { - var statErr error - var cmdOutput string - // Check to see if it's been pushed (foobar.txt abd directory testdir) - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + It("should create pvc and reuse if it shares the same devfile volume name", func() { + var statErr error + var cmdOutput string + // Check to see if it's been pushed (foobar.txt abd directory testdir) + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime2", - commonVar.Project, - []string{"cat", "/myvol/myfile.log"}, - func(cmdOp string, err error) bool { - cmdOutput = cmdOp - statErr = err - return err == nil - }, - ) - Expect(statErr).ToNot(HaveOccurred()) - Expect(cmdOutput).To(ContainSubstring("hello")) + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime2", + commonVar.Project, + []string{"cat", "/myvol/myfile.log"}, + func(cmdOp string, err error) bool { + cmdOutput = cmdOp + statErr = err + return err == nil + }, + ) + Expect(statErr).ToNot(HaveOccurred()) + Expect(cmdOutput).To(ContainSubstring("hello")) - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime2", - commonVar.Project, - []string{"stat", "/data2"}, - func(cmdOp string, err error) bool { - statErr = err - return err == nil - }, - ) - Expect(statErr).ToNot(HaveOccurred()) - }) + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime2", + commonVar.Project, + []string{"stat", "/data2"}, + func(cmdOp string, err error) bool { + statErr = err + return err == nil + }, + ) + Expect(statErr).ToNot(HaveOccurred()) + }) - It("check the volume name and mount paths for the containers", func() { - deploymentName, err := util.NamespaceKubernetesObject(devfileCmpName, "app") - Expect(err).To(BeNil()) + It("check the volume name and mount paths for the containers", func() { + deploymentName, err := util.NamespaceKubernetesObject(devfileCmpName, "app") + Expect(err).To(BeNil()) - volumesMatched := false + volumesMatched := false - // check the volume name and mount paths for the containers - volNamesAndPaths := commonVar.CliRunner.GetVolumeMountNamesandPathsFromContainer(deploymentName, "runtime", commonVar.Project) - volNamesAndPathsArr := strings.Fields(volNamesAndPaths) - for _, volNamesAndPath := range volNamesAndPathsArr { - volNamesAndPathArr := strings.Split(volNamesAndPath, ":") + // check the volume name and mount paths for the containers + volNamesAndPaths := commonVar.CliRunner.GetVolumeMountNamesandPathsFromContainer(deploymentName, "runtime", commonVar.Project) + volNamesAndPathsArr := strings.Fields(volNamesAndPaths) + for _, volNamesAndPath := range volNamesAndPathsArr { + volNamesAndPathArr := strings.Split(volNamesAndPath, ":") - if strings.Contains(volNamesAndPathArr[0], "myvol") && volNamesAndPathArr[1] == "/data" { - volumesMatched = true + if strings.Contains(volNamesAndPathArr[0], "myvol") && volNamesAndPathArr[1] == "/data" { + volumesMatched = true + } } - } - Expect(volumesMatched).To(Equal(true)) - }) - }) - - When("running odo dev with devfile containing volume-component", func() { - devfileCmpName := "test-devfile" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-volume-components.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + Expect(volumesMatched).To(Equal(true)) + }) + }) + } + + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "test-devfile" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("running odo dev with devfile containing volume-component - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-with-volume-components.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) - It("should successfully use the volume components in container components", func() { + It("should successfully use the volume components in container components", func() { - // Verify the pvc size for firstvol - storageSize := commonVar.CliRunner.GetPVCSize(devfileCmpName, "firstvol", commonVar.Project) - // should be the default size - Expect(storageSize).To(ContainSubstring("1Gi")) + // Verify the pvc size for firstvol + storageSize := commonVar.CliRunner.GetPVCSize(devfileCmpName, "firstvol", commonVar.Project) + // should be the default size + Expect(storageSize).To(ContainSubstring("1Gi")) - // Verify the pvc size for secondvol - storageSize = commonVar.CliRunner.GetPVCSize(devfileCmpName, "secondvol", commonVar.Project) - // should be the specified size in the devfile volume component - Expect(storageSize).To(ContainSubstring("200Mi")) + // Verify the pvc size for secondvol + storageSize = commonVar.CliRunner.GetPVCSize(devfileCmpName, "secondvol", commonVar.Project) + // should be the specified size in the devfile volume component + Expect(storageSize).To(ContainSubstring("200Mi")) + }) }) - }) + } Describe("devfile contains composite apply command", func() { const ( @@ -1325,244 +1499,289 @@ CMD ["npm", "start"] })) url = server.URL - helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), "./Dockerfile", url) - }) - - AfterEach(func() { - server.Close() - }) - - It("should not build images when odo dev is run", func() { - _, sessionOut, _, err := helper.DevModeShouldFail(env, "failed to retrieve "+url) - Expect(err).To(BeNil()) - Expect(sessionOut).NotTo(ContainSubstring("build -t quay.io/unknown-account/myimage -f ")) - Expect(sessionOut).NotTo(ContainSubstring("push quay.io/unknown-account/myimage")) - }) - }) - } - }) - }) - - When("running odo dev and devfile with composite command", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeCommands.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) - - It("should execute all commands in composite command", func() { - // Verify the command executed successfully - var statErr error - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime", - commonVar.Project, - []string{"stat", "/projects/testfolder"}, - func(cmdOp string, err error) bool { - statErr = err - return err == nil - }, - ) - Expect(statErr).ToNot(HaveOccurred()) - }) - }) - - When("running odo dev and composite command is marked as parallel:true", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeCommandsParallel.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) - - It("should execute all commands in composite command", func() { - // Verify the command executed successfully - var statErr error - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime", - commonVar.Project, - []string{"stat", "/projects/testfolder"}, - func(cmdOp string, err error) bool { - statErr = err - return err == nil - }, - ) - Expect(statErr).ToNot(HaveOccurred()) - }) - }) - - When("running odo dev and composite command are nested", func() { - devfileCmpName := "nodejs" - var session helper.DevSession - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileNestedCompCommands.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - var err error - session, _, _, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) - - It("should execute all commands in composite commmand", func() { - // Verify the command executed successfully - var statErr error - podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - podName, - "runtime", - commonVar.Project, - []string{"stat", "/projects/testfolder"}, - func(cmdOp string, err error) bool { - statErr = err - return err == nil - }, - ) - Expect(statErr).ToNot(HaveOccurred()) - }) - }) - - When("running odo dev and composite command is used as a run command", func() { - var session helper.DevSession - var stdout []byte - var stderr []byte - const devfileCmpName = "nodejs" - BeforeEach(func() { - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeRunAndDebug.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - var err error - session, stdout, stderr, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) + helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), "./Dockerfile", url) + }) - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) + AfterEach(func() { + server.Close() + }) - It("should run successfully", func() { - By("verifying from the output that all commands have been executed", func() { - helper.MatchAllInOutput(string(stdout), []string{ - "Building your application in container on cluster", - "Executing the application (command: mkdir)", - "Executing the application (command: echo)", - "Executing the application (command: install)", - "Executing the application (command: start)", + It("should not build images when odo dev is run", func() { + _, sessionOut, _, err := helper.DevModeShouldFail(env, "failed to retrieve "+url) + Expect(err).To(BeNil()) + Expect(sessionOut).NotTo(ContainSubstring("build -t quay.io/unknown-account/myimage -f ")) + Expect(sessionOut).NotTo(ContainSubstring("push quay.io/unknown-account/myimage")) + }) }) - }) + } + }) + }) - By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { - helper.MatchAllInOutput(string(stderr), []string{ - "Devfile command \"echo\" exited with an error status", - "intentional-error-message", - }) + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "nodejs" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("running odo dev and devfile with composite command - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeCommands.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) }) - - By("building the application only once", func() { - // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. - // The bracket allows to match the last occurrence with the command execution timing information. - Expect(strings.Count(string(stdout), "Building your application in container on cluster (command: install) [")). - To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") + AfterEach(func() { + session.Stop() + session.WaitEnd() }) - By("verifying that the command did run successfully", func() { + It("should execute all commands in composite command", func() { + // Verify the command executed successfully + var statErr error podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( podName, "runtime", commonVar.Project, []string{"stat", "/projects/testfolder"}, func(cmdOp string, err error) bool { + statErr = err return err == nil }, ) - Expect(res).To(BeTrue()) + Expect(statErr).ToNot(HaveOccurred()) }) }) - }) - - When("running build and run commands as composite in different containers and a shared volume", func() { - var session helper.DevSession - var stdout []byte - var stderr []byte - const devfileCmpName = "nodejs" - BeforeEach(func() { - helper.CopyExampleDevFile( - filepath.Join("source", "devfiles", "nodejs", "devfileCompositeBuildRunDebugInMultiContainersAndSharedVolume.yaml"), - filepath.Join(commonVar.Context, "devfile.yaml")) - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - var err error - session, stdout, stderr, _, err = helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - }) - - AfterEach(func() { - session.Stop() - session.WaitEnd() - }) - It("should run successfully", func() { - By("verifying from the output that all commands have been executed", func() { - helper.MatchAllInOutput(string(stdout), []string{ - "Building your application in container on cluster (command: mkdir)", - "Building your application in container on cluster (command: sleep-cmd-build)", - "Building your application in container on cluster (command: build-cmd)", - "Executing the application (command: sleep-cmd-run)", - "Executing the application (command: echo-with-error)", - "Executing the application (command: check-build-result)", - "Executing the application (command: start)", - }) + When("running odo dev and composite command is marked as parallel:true - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeCommandsParallel.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() }) - By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { - helper.MatchAllInOutput(string(stderr), []string{ - "Devfile command \"echo-with-error\" exited with an error status", - "intentional-error-message", - }) + It("should execute all commands in composite command", func() { + // Verify the command executed successfully + var statErr error + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime", + commonVar.Project, + []string{"stat", "/projects/testfolder"}, + func(cmdOp string, err error) bool { + statErr = err + return err == nil + }, + ) + Expect(statErr).ToNot(HaveOccurred()) }) + }) - By("building the application only once per exec command in the build command", func() { - // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. - // The bracket allows to match the last occurrence with the command execution timing information. - out := string(stdout) - for _, cmd := range []string{"mkdir", "sleep-cmd-build", "build-cmd"} { - Expect(strings.Count(out, fmt.Sprintf("Building your application in container on cluster (command: %s) [", cmd))). - To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") + When("running odo dev and composite command are nested - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + var session helper.DevSession + BeforeEach(func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileNestedCompCommands.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) } + var err error + session, _, _, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() }) - By("verifying that the command did run successfully", func() { + It("should execute all commands in composite commmand", func() { + // Verify the command executed successfully + var statErr error podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) - res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( podName, "runtime", commonVar.Project, []string{"stat", "/projects/testfolder"}, func(cmdOp string, err error) bool { + statErr = err return err == nil }, ) - Expect(res).To(BeTrue()) + Expect(statErr).ToNot(HaveOccurred()) }) }) - }) + + When("running odo dev and composite command is used as a run command - "+devfileHandlerCtx.name, func() { + var session helper.DevSession + var stdout []byte + var stderr []byte + var devfileCmpName string + BeforeEach(func() { + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfileCompositeRunAndDebug.yaml"), filepath.Join(commonVar.Context, "devfile.yaml")) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, stdout, stderr, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) + + It("should run successfully", func() { + By("verifying from the output that all commands have been executed", func() { + helper.MatchAllInOutput(string(stdout), []string{ + "Building your application in container on cluster", + "Executing the application (command: mkdir)", + "Executing the application (command: echo)", + "Executing the application (command: install)", + "Executing the application (command: start)", + }) + }) + + By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { + helper.MatchAllInOutput(string(stderr), []string{ + "Devfile command \"echo\" exited with an error status", + "intentional-error-message", + }) + }) + + By("building the application only once", func() { + // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. + // The bracket allows to match the last occurrence with the command execution timing information. + Expect(strings.Count(string(stdout), "Building your application in container on cluster (command: install) [")). + To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") + }) + + By("verifying that the command did run successfully", func() { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime", + commonVar.Project, + []string{"stat", "/projects/testfolder"}, + func(cmdOp string, err error) bool { + return err == nil + }, + ) + Expect(res).To(BeTrue()) + }) + }) + }) + + When("running build and run commands as composite in different containers and a shared volume - "+devfileHandlerCtx.name, func() { + var session helper.DevSession + var stdout []byte + var stderr []byte + var devfileCmpName string + BeforeEach(func() { + helper.CopyExampleDevFile( + filepath.Join("source", "devfiles", "nodejs", "devfileCompositeBuildRunDebugInMultiContainersAndSharedVolume.yaml"), + filepath.Join(commonVar.Context, "devfile.yaml")) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + var err error + session, stdout, stderr, _, err = helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) + + It("should run successfully", func() { + By("verifying from the output that all commands have been executed", func() { + helper.MatchAllInOutput(string(stdout), []string{ + "Building your application in container on cluster (command: mkdir)", + "Building your application in container on cluster (command: sleep-cmd-build)", + "Building your application in container on cluster (command: build-cmd)", + "Executing the application (command: sleep-cmd-run)", + "Executing the application (command: echo-with-error)", + "Executing the application (command: check-build-result)", + "Executing the application (command: start)", + }) + }) + + By("verifying that any command that did not succeed in the middle has logged such information correctly", func() { + helper.MatchAllInOutput(string(stderr), []string{ + "Devfile command \"echo-with-error\" exited with an error status", + "intentional-error-message", + }) + }) + + By("building the application only once per exec command in the build command", func() { + // Because of the Spinner, the "Building your application in container on cluster" is printed twice in the captured stdout. + // The bracket allows to match the last occurrence with the command execution timing information. + out := string(stdout) + for _, cmd := range []string{"mkdir", "sleep-cmd-build", "build-cmd"} { + Expect(strings.Count(out, fmt.Sprintf("Building your application in container on cluster (command: %s) [", cmd))). + To(BeNumerically("==", 1), "\nOUTPUT: "+string(stdout)+"\n") + } + }) + + By("verifying that the command did run successfully", func() { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project) + res := commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + podName, + "runtime", + commonVar.Project, + []string{"stat", "/projects/testfolder"}, + func(cmdOp string, err error) bool { + return err == nil + }, + ) + Expect(res).To(BeTrue()) + }) + }) + }) + } When("running odo dev and prestart events are defined", func() { BeforeEach(func() { @@ -1682,190 +1901,218 @@ CMD ["npm", "start"] }) }) - When("running odo dev with alternative commands", func() { - - type testCase struct { - buildCmd string - runCmd string - devAdditionalOpts []string - checkFunc func(stdout, stderr string) - } - testForCmd := func(tt testCase) { - err := helper.RunDevMode(tt.devAdditionalOpts, nil, func(session *gexec.Session, outContents []byte, errContents []byte, ports map[string]string) { - stdout := string(outContents) - stderr := string(errContents) - - By("checking the output of the command", func() { - helper.MatchAllInOutput(stdout, []string{ - fmt.Sprintf("Building your application in container on cluster (command: %s)", tt.buildCmd), - fmt.Sprintf("Executing the application (command: %s)", tt.runCmd), + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "nodejs" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("running odo dev with alternative commands - "+devfileHandlerCtx.name, func() { + + type testCase struct { + buildCmd string + runCmd string + devAdditionalOpts []string + checkFunc func(stdout, stderr string) + } + testForCmd := func(tt testCase) { + err := helper.RunDevMode(tt.devAdditionalOpts, nil, func(session *gexec.Session, outContents []byte, errContents []byte, ports map[string]string) { + stdout := string(outContents) + stderr := string(errContents) + + By("checking the output of the command", func() { + helper.MatchAllInOutput(stdout, []string{ + fmt.Sprintf("Building your application in container on cluster (command: %s)", tt.buildCmd), + fmt.Sprintf("Executing the application (command: %s)", tt.runCmd), + }) }) - }) - - if tt.checkFunc != nil { - tt.checkFunc(stdout, stderr) - } - - By("verifying the exposed application endpoint", func() { - url := fmt.Sprintf("http://%s", ports["3000"]) - resp, err := http.Get(url) - Expect(err).ToNot(HaveOccurred()) - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) - helper.MatchAllInOutput(string(body), []string{"Hello from Node.js Starter Application!"}) - }) + if tt.checkFunc != nil { + tt.checkFunc(stdout, stderr) + } - }) - Expect(err).ToNot(HaveOccurred()) - } + By("verifying the exposed application endpoint", func() { + url := fmt.Sprintf("http://%s", ports["3000"]) + resp, err := http.Get(url) + Expect(err).ToNot(HaveOccurred()) + defer resp.Body.Close() - remoteFileChecker := func(path string) bool { - return commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - commonVar.CliRunner.GetRunningPodNameByComponent("nodejs", commonVar.Project), - "runtime", - commonVar.Project, - []string{"stat", path}, - func(cmdOp string, err error) bool { - return err == nil - }, - ) - } + body, _ := io.ReadAll(resp.Body) + helper.MatchAllInOutput(string(body), []string{"Hello from Node.js Starter Application!"}) + }) - BeforeEach(func() { - helper.CopyExampleDevFile( - filepath.Join("source", "devfiles", "nodejs", "devfile-with-alternative-commands.yaml"), - filepath.Join(commonVar.Context, "devfile.yaml")) - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - }) + }) + Expect(err).ToNot(HaveOccurred()) + } - When("running odo dev with a build command", func() { - buildCmdTestFunc := func(buildCmd string, checkFunc func(stdout, stderr string)) { - testForCmd( - testCase{ - buildCmd: buildCmd, - runCmd: "devrun", - devAdditionalOpts: []string{"--build-command", buildCmd}, - checkFunc: checkFunc, + remoteFileChecker := func(path string) bool { + return commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + commonVar.CliRunner.GetRunningPodNameByComponent(devfileHandlerCtx.cmpNameProvider(), commonVar.Project), + "runtime", + commonVar.Project, + []string{"stat", path}, + func(cmdOp string, err error) bool { + return err == nil }, ) } - It("should error out if called with an invalid command", func() { - output := helper.Cmd("odo", "dev", "--random-ports", "--build-command", "build-command-does-not-exist").ShouldFail().Err() - Expect(output).To(ContainSubstring("no build command with name \"build-command-does-not-exist\" found in Devfile")) + BeforeEach(func() { + helper.CopyExampleDevFile( + filepath.Join("source", "devfiles", "nodejs", "devfile-with-alternative-commands.yaml"), + filepath.Join(commonVar.Context, "devfile.yaml")) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } }) - It("should error out if called with a command of another kind", func() { - // devrun is a valid run command, not a build command - output := helper.Cmd("odo", "dev", "--random-ports", "--build-command", "devrun").ShouldFail().Err() - Expect(output).To(ContainSubstring("no build command with name \"devrun\" found in Devfile")) - }) + When("running odo dev with a build command", func() { + buildCmdTestFunc := func(buildCmd string, checkFunc func(stdout, stderr string)) { + testForCmd( + testCase{ + buildCmd: buildCmd, + runCmd: "devrun", + devAdditionalOpts: []string{"--build-command", buildCmd}, + checkFunc: checkFunc, + }, + ) + } + + It("should error out if called with an invalid command", func() { + output := helper.Cmd("odo", "dev", "--random-ports", "--build-command", "build-command-does-not-exist").ShouldFail().Err() + Expect(output).To(ContainSubstring("no build command with name \"build-command-does-not-exist\" found in Devfile")) + }) + + It("should error out if called with a command of another kind", func() { + // devrun is a valid run command, not a build command + output := helper.Cmd("odo", "dev", "--random-ports", "--build-command", "devrun").ShouldFail().Err() + Expect(output).To(ContainSubstring("no build command with name \"devrun\" found in Devfile")) + }) - It("should execute the custom non-default build command successfully", func() { - buildCmdTestFunc("my-custom-build", func(stdout, stderr string) { - By("checking that it did not execute the default build command", func() { - helper.DontMatchAllInOutput(stdout, []string{ - "Building your application in container on cluster (command: devbuild)", + It("should execute the custom non-default build command successfully", func() { + buildCmdTestFunc("my-custom-build", func(stdout, stderr string) { + By("checking that it did not execute the default build command", func() { + helper.DontMatchAllInOutput(stdout, []string{ + "Building your application in container on cluster (command: devbuild)", + }) }) - }) - By("verifying that the custom command ran successfully", func() { - Expect(remoteFileChecker("/projects/file-from-my-custom-build")).To(BeTrue()) + By("verifying that the custom command ran successfully", func() { + Expect(remoteFileChecker("/projects/file-from-my-custom-build")).To(BeTrue()) + }) }) }) - }) - It("should execute the default build command successfully if specified explicitly", func() { - // devbuild is the default build command - buildCmdTestFunc("devbuild", func(stdout, stderr string) { - By("checking that it did not execute the custom build command", func() { - helper.DontMatchAllInOutput(stdout, []string{ - "Building your application in container on cluster (command: my-custom-build)", + It("should execute the default build command successfully if specified explicitly", func() { + // devbuild is the default build command + buildCmdTestFunc("devbuild", func(stdout, stderr string) { + By("checking that it did not execute the custom build command", func() { + helper.DontMatchAllInOutput(stdout, []string{ + "Building your application in container on cluster (command: my-custom-build)", + }) }) }) }) }) - }) - When("running odo dev with a run command", func() { - runCmdTestFunc := func(runCmd string, checkFunc func(stdout, stderr string)) { - testForCmd( - testCase{ - buildCmd: "devbuild", - runCmd: runCmd, - devAdditionalOpts: []string{"--run-command", runCmd}, - checkFunc: checkFunc, - }, - ) - } + When("running odo dev with a run command", func() { + runCmdTestFunc := func(runCmd string, checkFunc func(stdout, stderr string)) { + testForCmd( + testCase{ + buildCmd: "devbuild", + runCmd: runCmd, + devAdditionalOpts: []string{"--run-command", runCmd}, + checkFunc: checkFunc, + }, + ) + } - It("should error out if called with an invalid command", func() { - output := helper.Cmd("odo", "dev", "--random-ports", "--run-command", "run-command-does-not-exist").ShouldFail().Err() - Expect(output).To(ContainSubstring("no run command with name \"run-command-does-not-exist\" found in Devfile")) - }) + It("should error out if called with an invalid command", func() { + output := helper.Cmd("odo", "dev", "--random-ports", "--run-command", "run-command-does-not-exist").ShouldFail().Err() + Expect(output).To(ContainSubstring("no run command with name \"run-command-does-not-exist\" found in Devfile")) + }) - It("should error out if called with a command of another kind", func() { - // devbuild is a valid build command, not a run command - output := helper.Cmd("odo", "dev", "--random-ports", "--run-command", "devbuild").ShouldFail().Err() - Expect(output).To(ContainSubstring("no run command with name \"devbuild\" found in Devfile")) - }) + It("should error out if called with a command of another kind", func() { + // devbuild is a valid build command, not a run command + output := helper.Cmd("odo", "dev", "--random-ports", "--run-command", "devbuild").ShouldFail().Err() + Expect(output).To(ContainSubstring("no run command with name \"devbuild\" found in Devfile")) + }) - It("should execute the custom non-default run command successfully", func() { - runCmdTestFunc("my-custom-run", func(stdout, stderr string) { - By("checking that it did not execute the default run command", func() { - helper.DontMatchAllInOutput(stdout, []string{ - "Executing the application (command: devrun)", + It("should execute the custom non-default run command successfully", func() { + runCmdTestFunc("my-custom-run", func(stdout, stderr string) { + By("checking that it did not execute the default run command", func() { + helper.DontMatchAllInOutput(stdout, []string{ + "Executing the application (command: devrun)", + }) }) - }) - By("verifying that the custom command ran successfully", func() { - Expect(remoteFileChecker("/projects/file-from-my-custom-run")).To(BeTrue()) + By("verifying that the custom command ran successfully", func() { + Expect(remoteFileChecker("/projects/file-from-my-custom-run")).To(BeTrue()) + }) }) }) - }) - It("should execute the default run command successfully if specified explicitly", func() { - // devrun is the default run command - runCmdTestFunc("devrun", func(stdout, stderr string) { - By("checking that it did not execute the custom run command", func() { - helper.DontMatchAllInOutput(stdout, []string{ - "Executing the application (command: my-custom-run)", + It("should execute the default run command successfully if specified explicitly", func() { + // devrun is the default run command + runCmdTestFunc("devrun", func(stdout, stderr string) { + By("checking that it did not execute the custom run command", func() { + helper.DontMatchAllInOutput(stdout, []string{ + "Executing the application (command: my-custom-run)", + }) }) }) }) }) - }) - It("should execute the custom non-default build and run commands successfully", func() { - buildCmd := "my-custom-build" - runCmd := "my-custom-run" + It("should execute the custom non-default build and run commands successfully", func() { + buildCmd := "my-custom-build" + runCmd := "my-custom-run" - testForCmd( - testCase{ - buildCmd: buildCmd, - runCmd: runCmd, - devAdditionalOpts: []string{"--build-command", buildCmd, "--run-command", runCmd}, - checkFunc: func(stdout, stderr string) { - By("checking that it did not execute the default build and run commands", func() { - helper.DontMatchAllInOutput(stdout, []string{ - "Building your application in container on cluster (command: devbuild)", - "Executing the application (command: devrun)", + testForCmd( + testCase{ + buildCmd: buildCmd, + runCmd: runCmd, + devAdditionalOpts: []string{"--build-command", buildCmd, "--run-command", runCmd}, + checkFunc: func(stdout, stderr string) { + By("checking that it did not execute the default build and run commands", func() { + helper.DontMatchAllInOutput(stdout, []string{ + "Building your application in container on cluster (command: devbuild)", + "Executing the application (command: devrun)", + }) }) - }) - By("verifying that the custom build command ran successfully", func() { - Expect(remoteFileChecker("/projects/file-from-my-custom-build")).To(BeTrue()) - }) + By("verifying that the custom build command ran successfully", func() { + Expect(remoteFileChecker("/projects/file-from-my-custom-build")).To(BeTrue()) + }) - By("verifying that the custom run command ran successfully", func() { - Expect(remoteFileChecker("/projects/file-from-my-custom-run")).To(BeTrue()) - }) + By("verifying that the custom run command ran successfully", func() { + Expect(remoteFileChecker("/projects/file-from-my-custom-run")).To(BeTrue()) + }) + }, }, - }, - ) + ) + }) }) - }) + } // Tests https://github.com/redhat-developer/odo/issues/3838 When("java-springboot application is created and running odo dev", func() { @@ -2133,40 +2380,66 @@ CMD ["npm", "start"] }) }) - When("a container component defines a Command or Args", func() { - devfileCmpName := "nodejs" - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile( - filepath.Join("source", "devfiles", "nodejs", "issue-5620-devfile-with-container-command-args.yaml"), - filepath.Join(commonVar.Context, "devfile.yaml")) - }) + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { return "nodejs" }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When("a container component defines a Command or Args - "+devfileHandlerCtx.name, func() { + var devfileCmpName string + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.CopyExampleDevFile( + filepath.Join("source", "devfiles", "nodejs", "issue-5620-devfile-with-container-command-args.yaml"), + filepath.Join(commonVar.Context, "devfile.yaml")) + devfileCmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(filepath.Join(commonVar.Context, "devfile.yaml")) + } + }) - It("should run odo dev successfully (#5620)", func() { - devSession, stdoutBytes, stderrBytes, _, err := helper.StartDevMode(nil) - Expect(err).ShouldNot(HaveOccurred()) - defer devSession.Stop() - const errorMessage = "Failed to create the component:" - helper.DontMatchAllInOutput(string(stdoutBytes), []string{errorMessage}) - helper.DontMatchAllInOutput(string(stderrBytes), []string{errorMessage}) + It("should run odo dev successfully (#5620)", func() { + devSession, stdoutBytes, stderrBytes, _, err := helper.StartDevMode(nil) + Expect(err).ShouldNot(HaveOccurred()) + defer devSession.Stop() + const errorMessage = "Failed to create the component:" + helper.DontMatchAllInOutput(string(stdoutBytes), []string{errorMessage}) + helper.DontMatchAllInOutput(string(stderrBytes), []string{errorMessage}) - // the command has been started directly in the background. Check the PID stored in a specific file. - commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( - commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project), - "runtime", - commonVar.Project, - []string{ - remotecmd.ShellExecutable, "-c", - fmt.Sprintf("kill -0 $(cat %s/.odo_cmd_run.pid) 2>/dev/null ; echo -n $?", - strings.TrimSuffix(storage.SharedDataMountPath, "/")), - }, - func(stdout string, err error) bool { - Expect(err).ShouldNot(HaveOccurred()) - Expect(stdout).To(Equal("0")) - return err == nil - }) + // the command has been started directly in the background. Check the PID stored in a specific file. + commonVar.CliRunner.CheckCmdOpInRemoteDevfilePod( + commonVar.CliRunner.GetRunningPodNameByComponent(devfileCmpName, commonVar.Project), + "runtime", + commonVar.Project, + []string{ + remotecmd.ShellExecutable, "-c", + fmt.Sprintf("kill -0 $(cat %s/.odo_cmd_run.pid) 2>/dev/null ; echo -n $?", + strings.TrimSuffix(storage.SharedDataMountPath, "/")), + }, + func(stdout string, err error) bool { + Expect(err).ShouldNot(HaveOccurred()) + Expect(stdout).To(Equal("0")) + return err == nil + }) + }) }) - }) + } When("a component with multiple endpoints is run", func() { stateFile := ".odo/devstate.json" diff --git a/tests/integration/cmd_devfile_deploy_test.go b/tests/integration/cmd_devfile_deploy_test.go index a1997713d91..129408243d3 100644 --- a/tests/integration/cmd_devfile_deploy_test.go +++ b/tests/integration/cmd_devfile_deploy_test.go @@ -67,90 +67,118 @@ var _ = Describe("odo devfile deploy command tests", func() { // this is a workaround to ensure that the for loop works with `It` blocks ctx := ctx - When(ctx.title, func() { - // from devfile - cmpName := "nodejs-prj1-api-abhz" - deploymentName := "my-component" - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context) - helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", ctx.devfileName), - path.Join(commonVar.Context, "devfile.yaml")) - if ctx.setupFunc != nil { - ctx.setupFunc() - } - }) - - When("running odo deploy", func() { - var stdout string + for _, devfileHandlerCtx := range []struct { + name string + cmpNameProvider func() string + devfileHandler func(path string) + }{ + { + name: "with metadata.name", + cmpNameProvider: func() string { + // from devfile + return "nodejs-prj1-api-abhz" + }, + }, + { + name: "without metadata.name", + cmpNameProvider: func() string { + //x prefix is added by util.GetDNS1123Name function + return "x" + filepath.Base(commonVar.Context) + }, + devfileHandler: func(path string) { + helper.UpdateDevfileContent(path, []helper.DevfileUpdater{helper.DevfileMetadataNameRemover}) + }, + }, + } { + devfileHandlerCtx := devfileHandlerCtx + When(ctx.title+" - "+devfileHandlerCtx.name, func() { + var cmpName string + deploymentName := "my-component" BeforeEach(func() { - stdout = helper.Cmd("odo", "deploy").AddEnv("PODMAN_CMD=echo").ShouldPass().Out() - // An ENV file should have been created indicating current namespace - Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeTrue()) - helper.FileShouldContainSubstring(".odo/env/env.yaml", "Project: "+commonVar.Project) - }) - It("should succeed", func() { - By("building and pushing image to registry", func() { - Expect(stdout).To(ContainSubstring("build -t quay.io/unknown-account/myimage -f " + - filepath.Join(commonVar.Context, "Dockerfile ") + commonVar.Context)) - Expect(stdout).To(ContainSubstring("push quay.io/unknown-account/myimage")) - }) - By("deploying a deployment with the built image", func() { - out := commonVar.CliRunner.Run("get", "deployment", deploymentName, "-n", - commonVar.Project, "-o", `jsonpath="{.spec.template.spec.containers[0].image}"`).Wait().Out.Contents() - Expect(out).To(ContainSubstring("quay.io/unknown-account/myimage")) - }) - }) - - It("should run odo dev successfully", func() { - session, _, _, _, err := helper.StartDevMode(nil) - Expect(err).ToNot(HaveOccurred()) - session.Kill() - session.WaitEnd() + helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context) + helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", ctx.devfileName), + path.Join(commonVar.Context, "devfile.yaml")) + cmpName = devfileHandlerCtx.cmpNameProvider() + if devfileHandlerCtx.devfileHandler != nil { + devfileHandlerCtx.devfileHandler(path.Join(commonVar.Context, "devfile.yaml")) + } + if ctx.setupFunc != nil { + ctx.setupFunc() + } }) - When("deleting previous deployment and switching kubeconfig to another namespace", func() { - var otherNS string + When("running odo deploy", func() { + var stdout string BeforeEach(func() { - helper.Cmd("odo", "delete", "component", "--name", cmpName, "-f").ShouldPass() - output := commonVar.CliRunner.Run("get", "deployment", "-n", commonVar.Project).Err.Contents() - Expect(string(output)).To( - ContainSubstring("No resources found in " + commonVar.Project + " namespace.")) - - otherNS = commonVar.CliRunner.CreateAndSetRandNamespaceProject() + stdout = helper.Cmd("odo", "deploy").AddEnv("PODMAN_CMD=echo").ShouldPass().Out() + // An ENV file should have been created indicating current namespace + Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeTrue()) + helper.FileShouldContainSubstring(".odo/env/env.yaml", "Project: "+commonVar.Project) + }) + It("should succeed", func() { + By("building and pushing image to registry", func() { + Expect(stdout).To(ContainSubstring("build -t quay.io/unknown-account/myimage -f " + + filepath.Join(commonVar.Context, "Dockerfile ") + commonVar.Context)) + Expect(stdout).To(ContainSubstring("push quay.io/unknown-account/myimage")) + }) + By("deploying a deployment with the built image", func() { + out := commonVar.CliRunner.Run("get", "deployment", deploymentName, "-n", + commonVar.Project, "-o", `jsonpath="{.spec.template.spec.containers[0].image}"`).Wait().Out.Contents() + Expect(out).To(ContainSubstring("quay.io/unknown-account/myimage")) + }) }) - AfterEach(func() { - commonVar.CliRunner.DeleteNamespaceProject(otherNS, false) + It("should run odo dev successfully", func() { + session, _, _, _, err := helper.StartDevMode(nil) + Expect(err).ToNot(HaveOccurred()) + session.Kill() + session.WaitEnd() }) - It("should run odo deploy on initial namespace", func() { - helper.Cmd("odo", "deploy").AddEnv("PODMAN_CMD=echo").ShouldPass() + When("deleting previous deployment and switching kubeconfig to another namespace", func() { + var otherNS string + BeforeEach(func() { + helper.Cmd("odo", "delete", "component", "--name", cmpName, "-f").ShouldPass() + output := commonVar.CliRunner.Run("get", "deployment", "-n", commonVar.Project).Err.Contents() + Expect(string(output)).To( + ContainSubstring("No resources found in " + commonVar.Project + " namespace.")) - output := commonVar.CliRunner.Run("get", "deployment").Err.Contents() - Expect(string(output)).To( - ContainSubstring("No resources found in " + otherNS + " namespace.")) + otherNS = commonVar.CliRunner.CreateAndSetRandNamespaceProject() + }) - output = commonVar.CliRunner.Run("get", "deployment", "-n", commonVar.Project).Out.Contents() - Expect(string(output)).To(ContainSubstring(deploymentName)) - }) + AfterEach(func() { + commonVar.CliRunner.DeleteNamespaceProject(otherNS, false) + }) - }) + It("should run odo deploy on initial namespace", func() { + helper.Cmd("odo", "deploy").AddEnv("PODMAN_CMD=echo").ShouldPass() + + output := commonVar.CliRunner.Run("get", "deployment").Err.Contents() + Expect(string(output)).To( + ContainSubstring("No resources found in " + otherNS + " namespace.")) + + output = commonVar.CliRunner.Run("get", "deployment", "-n", commonVar.Project).Out.Contents() + Expect(string(output)).To(ContainSubstring(deploymentName)) + }) - When("running and stopping odo dev", func() { - BeforeEach(func() { - session, _, _, _, err := helper.StartDevMode(nil) - Expect(err).ShouldNot(HaveOccurred()) - session.Stop() - session.WaitEnd() }) - It("should not delete the resources created with odo deploy", func() { - output := commonVar.CliRunner.Run("get", "deployment", "-n", commonVar.Project).Out.Contents() - Expect(string(output)).To(ContainSubstring(deploymentName)) + When("running and stopping odo dev", func() { + BeforeEach(func() { + session, _, _, _, err := helper.StartDevMode(nil) + Expect(err).ShouldNot(HaveOccurred()) + session.Stop() + session.WaitEnd() + }) + + It("should not delete the resources created with odo deploy", func() { + output := commonVar.CliRunner.Run("get", "deployment", "-n", commonVar.Project).Out.Contents() + Expect(string(output)).To(ContainSubstring(deploymentName)) + }) }) }) }) - }) + } } When("using a devfile.yaml containing two deploy commands", func() { diff --git a/tests/integration/interactive_init_test.go b/tests/integration/interactive_init_test.go index bab4fb5def5..81dc8205adc 100644 --- a/tests/integration/interactive_init_test.go +++ b/tests/integration/interactive_init_test.go @@ -210,6 +210,52 @@ var _ = Describe("odo init interactive command tests", func() { }) }) }) + + When("alizer detection of javascript name", func() { + + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context) + Expect(helper.ListFilesInDir(commonVar.Context)).To( + SatisfyAll( + HaveLen(3), + ContainElements("Dockerfile", "package.json", "server.js"))) + }) + + FIt("should display node-echo name", func() { + language := "javascript" + projectType := "nodejs" + projectName := "node-echo" + + output, err := helper.RunInteractive([]string{"odo", "init"}, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Based on the files in the current directory odo detected") + + helper.ExpectString(ctx, fmt.Sprintf("Language: %s", language)) + + helper.ExpectString(ctx, fmt.Sprintf("Project type: %s", projectType)) + + helper.ExpectString(ctx, + fmt.Sprintf("The devfile \"%s\" from the registry \"DefaultDevfileRegistry\" will be downloaded.", projectType)) + + helper.ExpectString(ctx, "Is this correct") + helper.SendLine(ctx, "\n") + + helper.ExpectString(ctx, "Select container for which you want to change configuration") + helper.SendLine(ctx, "\n") + + helper.ExpectString(ctx, "Enter component name") + helper.SendLine(ctx, "\n") + + helper.ExpectString(ctx, fmt.Sprintf("Your new component '%s' is ready in the current directory", projectName)) + }) + Expect(err).To(BeNil()) + + lines, err := helper.ExtractLines(output) + Expect(err).To(BeNil()) + Expect(len(lines)).To(BeNumerically(">", 2)) + Expect(lines[len(lines)-1]).To(Equal(fmt.Sprintf("Your new component '%s' is ready in the current directory", projectName))) + + }) + }) }) It("should start downloading starter project only after all interactive questions have been asked", func() {