diff --git a/pkg/dev/podmandev/pod.go b/pkg/dev/podmandev/pod.go index 787715d14b7..d2e4efe8f7a 100644 --- a/pkg/dev/podmandev/pod.go +++ b/pkg/dev/podmandev/pod.go @@ -2,7 +2,8 @@ package podmandev import ( "fmt" - "math/rand" //#nosec + "math/rand" // #nosec + "sort" "time" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -39,6 +40,7 @@ func createPodFromComponent( debugCommand string, withHelperContainer bool, randomPorts bool, + customForwardedPorts []api.ForwardedPort, usedPorts []int, ) (*corev1.Pod, []api.ForwardedPort, error) { podTemplate, err := generator.GetPodTemplateSpec(devfileObj, generator.PodTemplateParams{}) @@ -50,7 +52,8 @@ func createPodFromComponent( return nil, nil, fmt.Errorf("no valid components found in the devfile") } - fwPorts, err := getPortMapping(devfileObj, debug, randomPorts, usedPorts) + var fwPorts []api.ForwardedPort + fwPorts, err = getPortMapping(devfileObj, debug, randomPorts, usedPorts, customForwardedPorts) if err != nil { return nil, nil, err } @@ -174,7 +177,7 @@ func getVolumeName(volume string, componentName string, appName string) string { return volume + "-" + componentName + "-" + appName } -func getPortMapping(devfileObj parser.DevfileObj, debug bool, randomPorts bool, usedPorts []int) ([]api.ForwardedPort, error) { +func getPortMapping(devfileObj parser.DevfileObj, debug bool, randomPorts bool, usedPorts []int, definedPorts []api.ForwardedPort) ([]api.ForwardedPort, error) { containerComponents, err := devfileObj.Data.GetComponents(common.DevfileOptions{ ComponentOptions: common.ComponentOptions{ComponentType: v1alpha2.ContainerComponentType}, }) @@ -190,6 +193,12 @@ func getPortMapping(devfileObj parser.DevfileObj, debug bool, randomPorts bool, } } + // this list makes sure that we ranged ports[20001-30001] do not coincide with a custom local port + customLocalPorts := make(map[int]struct{}) + for _, dPort := range definedPorts { + customLocalPorts[dPort.LocalPort] = struct{}{} + } + isPortUsedInContainer := func(p int) bool { for _, port := range existingContainerPorts { if p == port { @@ -199,12 +208,38 @@ func getPortMapping(devfileObj parser.DevfileObj, debug bool, randomPorts bool, return false } + // getCustomLocalPort analyzes the definedPorts i.e. custom port forwarding to see if a containerPort has a custom localPort, if a container name is provided, it also takes that into account. + getCustomLocalPort := func(containerPort int, container string) int { + for _, dp := range definedPorts { + if dp.ContainerPort == containerPort { + if dp.ContainerName != "" { + if dp.ContainerName == container { + return dp.LocalPort + } + } else { + return dp.LocalPort + } + } + } + return 0 + } + var result []api.ForwardedPort startPort := 20001 endPort := startPort + 10000 usedPortsCopy := make([]int, len(usedPorts)) copy(usedPortsCopy, usedPorts) - for containerName, endpoints := range ceMapping { + + // Prepare to iterate over the ceMapping in an orderly fashion + // This ensures we iterate over the ceMapping in the same way every time, obtain the same result every time and avoid any flakes with tests + var containers []string + for container := range ceMapping { + containers = append(containers, container) + } + sort.Strings(containers) + + for _, containerName := range containers { + endpoints := ceMapping[containerName] epLoop: for _, ep := range endpoints { portName := ep.Name @@ -215,14 +250,32 @@ func getPortMapping(devfileObj parser.DevfileObj, debug bool, randomPorts bool, continue } var freePort int - if randomPorts { + if len(definedPorts) != 0 { + freePort = getCustomLocalPort(ep.TargetPort, containerName) + if freePort == 0 { + for { + freePort, err = util.NextFreePort(startPort, endPort, usedPorts) + if err != nil { + klog.Infof("%s", err) + continue + } + // ensure that freePort is not a custom local port + if _, isPortUsed := customLocalPorts[freePort]; isPortUsed { + startPort = freePort + 1 + continue + } + break + } + startPort = freePort + 1 + } + } else if randomPorts { if len(usedPortsCopy) != 0 { freePort = usedPortsCopy[0] usedPortsCopy = usedPortsCopy[1:] } else { - rand.Seed(time.Now().UnixNano()) //#nosec + rand.Seed(time.Now().UnixNano()) // #nosec for { - freePort = rand.Intn(endPort-startPort+1) + startPort //#nosec + freePort = rand.Intn(endPort-startPort+1) + startPort // #nosec if !isPortUsedInContainer(freePort) && util.IsPortFree(freePort) { break } diff --git a/pkg/dev/podmandev/pod_test.go b/pkg/dev/podmandev/pod_test.go index fa6d48a92cc..9ec1236aa11 100644 --- a/pkg/dev/podmandev/pod_test.go +++ b/pkg/dev/podmandev/pod_test.go @@ -35,7 +35,9 @@ var ( baseComponent = generator.GetContainerComponent(generator.ContainerComponentParams{ Name: "mycomponent", Container: v1alpha2.Container{ - Image: "myimage", + Image: "myimage", + Args: []string{"-f", "/dev/null"}, + Command: []string{"tail"}, }, }) @@ -127,14 +129,15 @@ func buildBasePod(withPfContainer bool) *corev1.Pod { func Test_createPodFromComponent(t *testing.T) { type args struct { - devfileObj func() parser.DevfileObj - componentName string - appName string - debug bool - buildCommand string - runCommand string - debugCommand string - forwardLocalhost bool + devfileObj func() parser.DevfileObj + componentName string + appName string + debug bool + buildCommand string + runCommand string + debugCommand string + forwardLocalhost bool + customForwardedPorts []api.ForwardedPort } tests := []struct { name string @@ -944,6 +947,346 @@ func Test_createPodFromComponent(t *testing.T) { }, }, }, + + { + name: "basic component + application endpoint + debug endpoint - with debug / custom mapping for port forwarding with container name (customForwardedPorts)", + args: args{ + devfileObj: func() parser.DevfileObj { + data, _ := data.NewDevfileData(string(data.APISchemaVersion200)) + _ = data.AddCommands([]v1alpha2.Command{command}) + cmp := baseComponent.DeepCopy() + cmp.Name = "runtime" + cmp.Container.Endpoints = []v1alpha2.Endpoint{ + { + Name: "http-8080", + TargetPort: 8080, + }, + { + Name: "debug", + TargetPort: 5858, + }, + } + + cmp2 := baseComponent.DeepCopy() + cmp2.Name = "tools" + cmp2.Container.Endpoints = []v1alpha2.Endpoint{ + { + Name: "http-5000", + TargetPort: 5000, + }, + } + _ = data.AddComponents([]v1alpha2.Component{*cmp, *cmp2}) + return parser.DevfileObj{ + Data: data, + } + }, + componentName: devfileName, + appName: appName, + debug: true, + customForwardedPorts: []api.ForwardedPort{ + { + ContainerName: "runtime", + LocalPort: 8080, + ContainerPort: 8080, + }, + }, + }, + wantPod: func(basePod *corev1.Pod) *corev1.Pod { + pod := basePod.DeepCopy() + pod.Spec.Containers[0].Name = "runtime" + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "http-8080", + ContainerPort: 8080, + HostPort: 8080, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "debug", + ContainerPort: 5858, + HostPort: 20001, + Protocol: corev1.ProtocolTCP, + }, + } + container2 := pod.Spec.Containers[0].DeepCopy() + container2.Name = "tools" + container2.Ports = []corev1.ContainerPort{ + { + Name: "http-5000", + ContainerPort: 5000, + HostPort: 20002, + Protocol: corev1.ProtocolTCP, + }, + } + pod.Spec.Containers = append(pod.Spec.Containers, *container2) + return pod + }, + wantFwPorts: []api.ForwardedPort{ + { + Platform: "podman", + ContainerName: "runtime", + PortName: "http-8080", + LocalAddress: "127.0.0.1", + LocalPort: 8080, + ContainerPort: 8080, + IsDebug: false, + }, + { + Platform: "podman", + ContainerName: "runtime", + PortName: "debug", + LocalAddress: "127.0.0.1", + LocalPort: 20001, + ContainerPort: 5858, + IsDebug: true, + }, + { + Platform: "podman", + ContainerName: "tools", + PortName: "http-5000", + LocalAddress: "127.0.0.1", + LocalPort: 20002, + ContainerPort: 5000, + IsDebug: false, + }, + }, + }, + { + name: "basic component + application endpoint + debug endpoint - with debug / custom mapping for port forwarding without container name (customForwardedPorts)", + args: args{ + devfileObj: func() parser.DevfileObj { + data, _ := data.NewDevfileData(string(data.APISchemaVersion200)) + _ = data.AddCommands([]v1alpha2.Command{command}) + cmp := baseComponent.DeepCopy() + cmp.Name = "runtime" + cmp.Container.Endpoints = []v1alpha2.Endpoint{ + { + Name: "http-8080", + + TargetPort: 8080, + }, + { + Name: "debug", + TargetPort: 5858, + }, + } + cmp2 := baseComponent.DeepCopy() + cmp2.Name = "tools" + cmp2.Container.Endpoints = []v1alpha2.Endpoint{ + { + Name: "http-5000", + TargetPort: 5000, + }, + } + + _ = data.AddComponents([]v1alpha2.Component{*cmp, *cmp2}) + return parser.DevfileObj{ + Data: data, + } + }, + componentName: devfileName, + appName: appName, + debug: true, + customForwardedPorts: []api.ForwardedPort{ + { + LocalPort: 8080, + ContainerPort: 8080, + }, + { + LocalPort: 5000, + ContainerPort: 5000, + }, + }, + }, + wantPod: func(basePod *corev1.Pod) *corev1.Pod { + pod := basePod.DeepCopy() + pod.Spec.Containers[0].Name = "runtime" + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "http-8080", + ContainerPort: 8080, + HostPort: 8080, + Protocol: corev1.ProtocolTCP, + }, { + Name: "debug", + ContainerPort: 5858, + HostPort: 20001, + Protocol: corev1.ProtocolTCP, + }, + } + container2 := pod.Spec.Containers[0].DeepCopy() + container2.Name = "tools" + container2.Ports = []corev1.ContainerPort{ + { + Name: "http-5000", + ContainerPort: 5000, + HostPort: 5000, + Protocol: corev1.ProtocolTCP, + }, + } + pod.Spec.Containers = append(pod.Spec.Containers, *container2) + return pod + }, + wantFwPorts: []api.ForwardedPort{ + { + Platform: "podman", + ContainerName: "runtime", + PortName: "http-8080", + LocalAddress: "127.0.0.1", + LocalPort: 8080, + ContainerPort: 8080, + IsDebug: false, + }, + { + Platform: "podman", + ContainerName: "runtime", + PortName: "debug", + LocalAddress: "127.0.0.1", + LocalPort: 20001, + ContainerPort: 5858, + IsDebug: true, + }, + { + Platform: "podman", + ContainerName: "tools", + PortName: "http-5000", + LocalAddress: "127.0.0.1", + LocalPort: 5000, + ContainerPort: 5000, + IsDebug: false, + }, + }, + }, + { + name: "basic component + application endpoint + debug endpoint - with debug / custom mapping for port forwarding with local port in ranged [20001-30001] ports (customForwardedPorts)", + args: args{ + devfileObj: func() parser.DevfileObj { + data, _ := data.NewDevfileData(string(data.APISchemaVersion200)) + _ = data.AddCommands([]v1alpha2.Command{command}) + cmp := baseComponent.DeepCopy() + cmp.Name = "runtime" + cmp.Container.Endpoints = []v1alpha2.Endpoint{ + { + Name: "http-8080", + TargetPort: 8080, + }, + { + Name: "debug", + TargetPort: 5858, + }, + } + + cmp2 := baseComponent.DeepCopy() + cmp2.Name = "tools" + cmp2.Container.Endpoints = []v1alpha2.Endpoint{ + { + Name: "http-9000", + TargetPort: 9000, + }, + { + Name: "http-5000", + TargetPort: 5000, + }, + } + + _ = data.AddComponents([]v1alpha2.Component{*cmp, *cmp2}) + return parser.DevfileObj{ + Data: data, + } + }, + componentName: devfileName, + appName: appName, + debug: true, + customForwardedPorts: []api.ForwardedPort{ + { + LocalPort: 20001, + ContainerPort: 8080, + }, + { + LocalPort: 20002, + ContainerPort: 9000, + }, + { + LocalPort: 5000, + ContainerPort: 5000, + }, + }, + }, + wantPod: func(basePod *corev1.Pod) *corev1.Pod { + pod := basePod.DeepCopy() + pod.Spec.Containers[0].Name = "runtime" + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "http-8080", + ContainerPort: 8080, + HostPort: 20001, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "debug", + ContainerPort: 5858, + HostPort: 20003, + Protocol: corev1.ProtocolTCP, + }, + } + container2 := pod.Spec.Containers[0].DeepCopy() + container2.Name = "tools" + container2.Ports = []corev1.ContainerPort{ + { + Name: "http-9000", + ContainerPort: 9000, + HostPort: 20002, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "http-5000", + ContainerPort: 5000, + HostPort: 5000, + Protocol: corev1.ProtocolTCP, + }, + } + pod.Spec.Containers = append(pod.Spec.Containers, *container2) + return pod + }, + wantFwPorts: []api.ForwardedPort{ + { + Platform: "podman", + ContainerName: "runtime", + PortName: "http-8080", + LocalAddress: "127.0.0.1", + LocalPort: 20001, + ContainerPort: 8080, + IsDebug: false, + }, + { + Platform: "podman", + ContainerName: "runtime", + PortName: "debug", + LocalAddress: "127.0.0.1", + LocalPort: 20003, + ContainerPort: 5858, + IsDebug: true, + }, + { + Platform: "podman", + ContainerName: "tools", + PortName: "http-9000", + LocalAddress: "127.0.0.1", + LocalPort: 20002, + ContainerPort: 9000, + IsDebug: false, + }, + { + Platform: "podman", + ContainerName: "tools", + PortName: "http-5000", + LocalAddress: "127.0.0.1", + LocalPort: 5000, + ContainerPort: 5000, + IsDebug: false, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -957,6 +1300,7 @@ func Test_createPodFromComponent(t *testing.T) { tt.args.debugCommand, tt.args.forwardLocalhost, false, + tt.args.customForwardedPorts, []int{20001, 20002, 20003, 20004, 20005}, ) if (err != nil) != tt.wantErr { @@ -968,7 +1312,8 @@ func Test_createPodFromComponent(t *testing.T) { if diff := cmp.Diff(tt.wantPod(basePod), got, cmpopts.EquateEmpty()); diff != "" { t.Errorf("createPodFromComponent() pod mismatch (-want +got):\n%s", diff) } - if diff := cmp.Diff(tt.wantFwPorts, gotFwPorts, cmpopts.EquateEmpty()); diff != "" { + + if diff := cmp.Diff(tt.wantFwPorts, gotFwPorts, cmpopts.EquateEmpty(), cmpopts.SortSlices(func(x, y api.ForwardedPort) bool { return x.ContainerName < y.ContainerName })); diff != "" { t.Errorf("createPodFromComponent() fwPorts mismatch (-want +got):\n%s", diff) } }) diff --git a/pkg/dev/podmandev/podmandev.go b/pkg/dev/podmandev/podmandev.go index d8c13856138..27469b220d2 100644 --- a/pkg/dev/podmandev/podmandev.go +++ b/pkg/dev/podmandev/podmandev.go @@ -101,25 +101,26 @@ func (o *DevClient) Start( watch.PrintInfoMessage(out, path, options.WatchFiles, promptMessage) watchParameters := watch.WatchParameters{ - DevfilePath: devfilePath, - Path: path, - ComponentName: componentName, - ApplicationName: appName, - InitialDevfileObj: *devfileObj, - DevfileWatchHandler: o.watchHandler, - FileIgnores: options.IgnorePaths, - Debug: options.Debug, - DevfileBuildCmd: options.BuildCommand, - DevfileRunCmd: options.RunCommand, - Variables: options.Variables, - RandomPorts: options.RandomPorts, - IgnoreLocalhost: options.IgnoreLocalhost, - ForwardLocalhost: options.ForwardLocalhost, - WatchFiles: options.WatchFiles, - WatchCluster: false, - Out: out, - ErrOut: errOut, - PromptMessage: promptMessage, + DevfilePath: devfilePath, + Path: path, + ComponentName: componentName, + ApplicationName: appName, + InitialDevfileObj: *devfileObj, + DevfileWatchHandler: o.watchHandler, + FileIgnores: options.IgnorePaths, + Debug: options.Debug, + DevfileBuildCmd: options.BuildCommand, + DevfileRunCmd: options.RunCommand, + Variables: options.Variables, + RandomPorts: options.RandomPorts, + IgnoreLocalhost: options.IgnoreLocalhost, + ForwardLocalhost: options.ForwardLocalhost, + CustomForwardedPorts: options.CustomForwardedPorts, + WatchFiles: options.WatchFiles, + WatchCluster: false, + Out: out, + ErrOut: errOut, + PromptMessage: promptMessage, } return o.watchClient.WatchAndPush(out, watchParameters, ctx, componentStatus) @@ -196,15 +197,16 @@ func (o *DevClient) watchHandler(ctx context.Context, pushParams adapters.PushPa printWarningsOnDevfileChanges(ctx, watchParams) startOptions := dev.StartOptions{ - IgnorePaths: watchParams.FileIgnores, - Debug: watchParams.Debug, - BuildCommand: watchParams.DevfileBuildCmd, - RunCommand: watchParams.DevfileRunCmd, - RandomPorts: watchParams.RandomPorts, - IgnoreLocalhost: watchParams.IgnoreLocalhost, - ForwardLocalhost: watchParams.ForwardLocalhost, - WatchFiles: watchParams.WatchFiles, - Variables: watchParams.Variables, + IgnorePaths: watchParams.FileIgnores, + Debug: watchParams.Debug, + BuildCommand: watchParams.DevfileBuildCmd, + RunCommand: watchParams.DevfileRunCmd, + RandomPorts: watchParams.RandomPorts, + IgnoreLocalhost: watchParams.IgnoreLocalhost, + ForwardLocalhost: watchParams.ForwardLocalhost, + CustomForwardedPorts: watchParams.CustomForwardedPorts, + WatchFiles: watchParams.WatchFiles, + Variables: watchParams.Variables, } return o.reconcile(ctx, watchParams.Out, watchParams.ErrOut, startOptions, componentStatus) } diff --git a/pkg/dev/podmandev/reconcile.go b/pkg/dev/podmandev/reconcile.go index 44a81f95cf7..73844f5fdd3 100644 --- a/pkg/dev/podmandev/reconcile.go +++ b/pkg/dev/podmandev/reconcile.go @@ -204,6 +204,7 @@ func (o *DevClient) deployPod(ctx context.Context, options dev.StartOptions) (*c options.DebugCommand, options.ForwardLocalhost, options.RandomPorts, + options.CustomForwardedPorts, o.usedPorts, ) if err != nil { diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index e6135627552..a9f34f703ca 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -4,16 +4,18 @@ import ( "context" "errors" "fmt" - "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - "github.com/redhat-developer/odo/pkg/api" "io" - "k8s.io/klog" "path/filepath" "regexp" "sort" "strconv" "strings" + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "k8s.io/klog" + + "github.com/redhat-developer/odo/pkg/api" + "github.com/spf13/cobra" ktemplates "k8s.io/kubectl/pkg/util/templates" @@ -200,11 +202,6 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { genericclioptions.WarnIfDefaultNamespace(odocontext.GetNamespace(ctx), o.clientset.KubernetesClient) } - // TODO: Remove this once --port-forward has been implemented for podman. - if platform == commonflags.PlatformPodman && len(o.portForwardFlag) != 0 { - fmt.Println() - log.Warning("--port-forward flag has not been implemented for podman yet, we will use the normal port forwarding") - } // check for .gitignore file and add odo-file-index.json to .gitignore. // In case the .gitignore was created by odo, it is purposely not reported as candidate for deletion (via a call to files.ReportLocalFileGeneratedByOdo) // because a .gitignore file is more likely to be modified by the user afterward (for another usage). diff --git a/tests/integration/cmd_dev_debug_test.go b/tests/integration/cmd_dev_debug_test.go index ea9da402b7d..859ca5ae628 100644 --- a/tests/integration/cmd_dev_debug_test.go +++ b/tests/integration/cmd_dev_debug_test.go @@ -2,10 +2,11 @@ package integration import ( "fmt" - "k8s.io/utils/pointer" "path/filepath" "strings" + "k8s.io/utils/pointer" + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/redhat-developer/odo/pkg/labels" @@ -40,47 +41,54 @@ var _ = Describe("odo dev debug command tests", func() { helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-debugrun.yaml")).ShouldPass() Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeFalse()) }) - if !podman { - // TODO(pvala): Refactor this when custom port-mapping for port forwarding has been implemented for podman - When("running odo dev with debug flag and custom port mapping for port forwarding", func() { - var devSession helper.DevSession - var ports map[string]string - var ( - LocalPort = helper.GetRandomFreePort() - LocalDebugPort = helper.GetRandomFreePort() - ) - const ( - ContainerPort = "3000" - ContainerDebugPort = "5858" - ) - BeforeEach(func() { - opts := []string{"--debug", fmt.Sprintf("--port-forward=%s:%s", LocalPort, ContainerPort), fmt.Sprintf("--port-forward=%s:%s", LocalDebugPort, ContainerDebugPort)} - var err error - devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ - CmdlineArgs: opts, - NoRandomPorts: true, - }) - Expect(err).ToNot(HaveOccurred()) + When("running odo dev with debug flag and custom port mapping for port forwarding", helper.LabelPodmanIf(podman, func() { + var ( + devSession helper.DevSession + ports map[string]string + ) + var ( + LocalPort = helper.GetRandomFreePort() + LocalDebugPort = helper.GetRandomFreePort() + ) + const ( + ContainerPort = "3000" + ContainerDebugPort = "5858" + ) + + BeforeEach(func() { + opts := []string{"--debug", fmt.Sprintf("--port-forward=%s:%s", LocalPort, ContainerPort), fmt.Sprintf("--port-forward=%s:%s", LocalDebugPort, ContainerDebugPort)} + if podman { + opts = append(opts, "--forward-localhost") + } + var err error + devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ + CmdlineArgs: opts, + NoRandomPorts: true, + RunOnPodman: podman, }) + Expect(err).ToNot(HaveOccurred()) + }) - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + + It("should connect to relevant custom ports forwarded", func() { + By("connecting to the application port", func() { + helper.HttpWaitForWithStatus("http://"+ports[ContainerPort], "Hello from Node.js Starter Application!", 12, 5, 200) }) - It("should connect to relevant custom ports forwarded", func() { - By("connecting to the application port", func() { - helper.HttpWaitForWithStatus("http://"+ports[ContainerPort], "Hello from Node.js Starter Application!", 12, 5, 200) - }) - 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 - url := fmt.Sprintf("http://%s", ports[ContainerDebugPort]) - Expect(url).To(ContainSubstring(LocalDebugPort)) - helper.HttpWaitForWithStatus(url, "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 + url := fmt.Sprintf("http://%s", ports[ContainerDebugPort]) + Expect(url).To(ContainSubstring(LocalDebugPort)) + + helper.HttpWaitForWithStatus(url, "WebSockets request was expected", 12, 5, 400) }) }) - } + })) + When("running odo dev with debug flag", helper.LabelPodmanIf(podman, func() { var devSession helper.DevSession var ports map[string]string diff --git a/tests/integration/cmd_dev_test.go b/tests/integration/cmd_dev_test.go index aa0edf27401..737f2837dba 100644 --- a/tests/integration/cmd_dev_test.go +++ b/tests/integration/cmd_dev_test.go @@ -131,6 +131,15 @@ var _ = Describe("odo dev command tests", func() { }) Expect(err).ToNot(HaveOccurred()) })) + + It("should fail when using --random-ports and --port-forward together", helper.LabelPodmanIf(podman, func() { + args := []string{"dev", "--random-ports", "--port-forward=8000:3000"} + if podman { + args = append(args, "--platform", "podman") + } + errOut := helper.Cmd("odo", args...).ShouldFail().Err() + Expect(errOut).To(ContainSubstring("--random-ports and --port-forward cannot be used together")) + })) } It("ensure that index information is updated", func() { @@ -712,221 +721,11 @@ ComponentSettings: }) } - // TODO(pvala): Merge this into the test below once custom port mapping for port-forwarding has been implemented for podman. - Context("port-forwarding for the component with custom port mapping", func() { - When("a component is bootstrapped", func() { - BeforeEach(func() { - helper.Cmd("odo", "set", "project", commonVar.Project).ShouldPass() - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass() - }) - - It("should fail when using --random-ports and --port-forward together", func() { - errOut := helper.Cmd("odo", "dev", "--random-ports", "--port-forward=8000:3000").ShouldFail().Err() - Expect(errOut).To(ContainSubstring("--random-ports and --port-forward cannot be used together")) - }) - - }) - When("devfile has single endpoint", func() { - var ( - LocalPort = helper.GetRandomFreePort() - ) - const ( - ContainerPort = "3000" - ) - BeforeEach(func() { - helper.Cmd("odo", "set", "project", commonVar.Project).ShouldPass() - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass() - }) - - When("running odo dev", func() { - var devSession helper.DevSession - var ports map[string]string - BeforeEach(func() { - var err error - opts := []string{fmt.Sprintf("--port-forward=%s:%s", LocalPort, ContainerPort)} - devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ - CmdlineArgs: opts, - NoRandomPorts: true, - }) - Expect(err).ToNot(HaveOccurred()) - }) - - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() - }) - - It("should expose the endpoint on localhost", func() { - url := fmt.Sprintf("http://%s", ports[ContainerPort]) - Expect(url).To(ContainSubstring(LocalPort)) - 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!"}) - Expect(err).ToNot(HaveOccurred()) - }) - - When("modifying memoryLimit for container in Devfile", func() { - var stdout string - var stderr string - BeforeEach(func() { - src := "memoryLimit: 1024Mi" - dst := "memoryLimit: 1023Mi" - helper.ReplaceString("devfile.yaml", src, dst) - var err error - var stdoutBytes []byte - var stderrBytes []byte - stdoutBytes, stderrBytes, ports, err = devSession.WaitSync() - Expect(err).Should(Succeed()) - stdout = string(stdoutBytes) - stderr = string(stderrBytes) - }) - - It("should react on the Devfile modification", func() { - By("not warning users that odo dev needs to be restarted", func() { - warning := "Please restart 'odo dev'" - Expect(stdout).ShouldNot(ContainSubstring(warning)) - Expect(stderr).ShouldNot(ContainSubstring(warning)) - }) - By("updating the pod", func() { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project) - bufferOutput := commonVar.CliRunner.Run("get", "pods", podName, "-o", "jsonpath='{.spec.containers[0].resources.requests.memory}'").Out.Contents() - output := string(bufferOutput) - Expect(output).To(ContainSubstring("1023Mi")) - }) - - By("exposing the endpoint", func() { - Eventually(func(g Gomega) { - url := fmt.Sprintf("http://%s", ports[ContainerPort]) - Expect(url).To(ContainSubstring(LocalPort)) - resp, err := http.Get(url) - g.Expect(err).ToNot(HaveOccurred()) - defer resp.Body.Close() - - body, _ := io.ReadAll(resp.Body) - for _, i := range []string{"Hello from Node.js Starter Application!"} { - g.Expect(string(body)).To(ContainSubstring(i)) - } - g.Expect(err).ToNot(HaveOccurred()) - }).WithPolling(1 * time.Second).WithTimeout(20 * time.Second).Should(Succeed()) - }) - }) - }) - }) - }) - - When("devfile has multiple endpoints", func() { - var ( - LocalPort1 = helper.GetRandomFreePort() - LocalPort2 = helper.GetRandomFreePort() - LocalPort3 = helper.GetRandomFreePort() - ) - const ( - // ContainerPort are hard-coded from devfile-with-multiple-endpoints.yaml - // Note 1: Debug endpoints will not be exposed for this instance, so we do not add custom mapping for them. - // Note 2: We add custom mapping for all the endpoints so that none of them are assigned random ports from the 20001-30001 range; - // Note 2(contd.): this is to avoid a race condition where a test running in parallel is also assigned similar ranged port the one here, and we fail to access either of them. - ContainerPort1 = "3000" - ContainerPort2 = "4567" - ContainerPort3 = "7890" - ) - BeforeEach(func() { - helper.Cmd("odo", "set", "project", commonVar.Project).ShouldPass() - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project-with-multiple-endpoints"), commonVar.Context) - helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-multiple-endpoints.yaml")).ShouldPass() - }) - - When("running odo dev", func() { - var devSession helper.DevSession - var ports map[string]string - BeforeEach(func() { - opts := []string{fmt.Sprintf("--port-forward=%s:%s", LocalPort1, ContainerPort1), fmt.Sprintf("--port-forward=%s:%s", LocalPort2, ContainerPort2), fmt.Sprintf("--port-forward=%s:%s", LocalPort3, ContainerPort3)} - var err error - devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ - CmdlineArgs: opts, - NoRandomPorts: true, - }) - Expect(err).ToNot(HaveOccurred()) - }) - - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() - }) - - It("should expose all endpoints on localhost regardless of exposure", func() { - By("not exposing debug endpoints", func() { - for _, p := range []int{5005, 5006} { - _, found := ports[strconv.Itoa(p)] - Expect(found).To(BeFalse(), fmt.Sprintf("debug port %d should not be forwarded", p)) - } - }) - - getServerResponse := func(containerPort, localPort string) (string, error) { - url := fmt.Sprintf("http://%s", ports[containerPort]) - Expect(url).To(ContainSubstring(localPort)) - resp, err := http.Get(url) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, _ := io.ReadAll(resp.Body) - return string(body), nil - } - containerPorts := []string{ContainerPort1, ContainerPort2, ContainerPort3} - localPorts := []string{LocalPort1, LocalPort2, LocalPort3} - - for i := range containerPorts { - containerPort := containerPorts[i] - localPort := localPorts[i] - By(fmt.Sprintf("exposing a port targeting container port %s", containerPort), func() { - r, err := getServerResponse(containerPort, localPort) - Expect(err).ShouldNot(HaveOccurred()) - helper.MatchAllInOutput(r, []string{"Hello from Node.js Starter Application!"}) - }) - } - - helper.ReplaceString("server.js", "Hello from Node.js", "H3110 from Node.js") - - var stdout, stderr []byte - var err error - stdout, stderr, _, err = devSession.WaitSync() - Expect(err).Should(Succeed()) - - By("not warning users that odo dev needs to be restarted because the Devfile has not changed", func() { - warning := "Please restart 'odo dev'" - Expect(stdout).ShouldNot(ContainSubstring(warning)) - Expect(stderr).ShouldNot(ContainSubstring(warning)) - }) - - for i := range containerPorts { - containerPort := containerPorts[i] - localPort := localPorts[i] - By(fmt.Sprintf("returning the right response when querying port forwarded for container port %s", containerPort), - func() { - Eventually(func(g Gomega) string { - r, err := getServerResponse(containerPort, localPort) - g.Expect(err).ShouldNot(HaveOccurred()) - return r - }, 180, 10).Should(Equal("H3110 from Node.js Starter Application!")) - }) - } - }) - }) - - }) - }) - - for _, manual := range []bool{false, true} { - for _, podman := range []bool{false, true} { - manual := manual - podman := podman - Context("port-forwarding for the component", helper.LabelPodmanIf(podman, func() { + for _, podman := range []bool{true, false} { + podman := podman + Context("port-forwarding for the component", helper.LabelPodmanIf(podman, func() { + for _, manual := range []bool{true, false} { + manual := manual When("devfile has no endpoint", func() { BeforeEach(func() { if !podman { @@ -957,212 +756,258 @@ ComponentSettings: devSession.WaitEnd() }) - It("should have no endpoint forwarded", func() { + It(fmt.Sprintf("should have no endpoint forwarded (podman=%v, manual=%v)", podman, manual), func() { Expect(ports).To(BeEmpty()) }) }) }) - When("devfile has single endpoint", func() { - BeforeEach(func() { - if !podman { - helper.Cmd("odo", "set", "project", commonVar.Project).ShouldPass() - } - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass() - }) - - When("running odo dev", func() { - var devSession helper.DevSession - var ports map[string]string + for _, customPortForwarding := range []bool{true, false} { + customPortForwarding := customPortForwarding + var NoRandomPorts bool + if customPortForwarding { + NoRandomPorts = true + } + When("devfile has single endpoint", func() { + var ( + LocalPort = helper.GetRandomFreePort() + ) + const ( + ContainerPort = "3000" + ) BeforeEach(func() { - var err error - opts := []string{} - if manual { - opts = append(opts, "--no-watch") - } - devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ - CmdlineArgs: opts, - RunOnPodman: podman, - }) - Expect(err).ToNot(HaveOccurred()) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) + helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass() }) - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() - }) - - It("should expose the endpoint on localhost", 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!"}) - Expect(err).ToNot(HaveOccurred()) - }) - - When("modifying memoryLimit for container in Devfile", func() { - var stdout string - var stderr string + When("running odo dev", func() { + var devSession helper.DevSession + var ports map[string]string BeforeEach(func() { - src := "memoryLimit: 1024Mi" - dst := "memoryLimit: 1023Mi" - helper.ReplaceString("devfile.yaml", src, dst) + var err error + opts := []string{} + if customPortForwarding { + opts = []string{fmt.Sprintf("--port-forward=%s:%s", LocalPort, ContainerPort)} + } if manual { - if os.Getenv("SKIP_KEY_PRESS") == "true" { - Skip("This is a unix-terminal specific scenario, skipping") - } - - devSession.PressKey('p') + opts = append(opts, "--no-watch") } - var err error - var stdoutBytes []byte - var stderrBytes []byte - stdoutBytes, stderrBytes, ports, err = devSession.WaitSync() - Expect(err).Should(Succeed()) - stdout = string(stdoutBytes) - stderr = string(stderrBytes) + devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ + CmdlineArgs: opts, + NoRandomPorts: NoRandomPorts, + RunOnPodman: podman, + }) + Expect(err).ToNot(HaveOccurred()) }) - It("should react on the Devfile modification", func() { - if podman { - By("warning users that odo dev needs to be restarted", func() { - Expect(stdout).To(ContainSubstring( - "Detected changes in the Devfile, but this is not supported yet on Podman. Please restart 'odo dev' for such changes to be applied.")) - }) - } else { - By("not warning users that odo dev needs to be restarted", func() { - warning := "Please restart 'odo dev'" - Expect(stdout).ShouldNot(ContainSubstring(warning)) - Expect(stderr).ShouldNot(ContainSubstring(warning)) - }) - By("updating the pod", func() { - podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project) - bufferOutput := commonVar.CliRunner.Run("get", "pods", podName, "-o", "jsonpath='{.spec.containers[0].resources.requests.memory}'").Out.Contents() - output := string(bufferOutput) - Expect(output).To(ContainSubstring("1023Mi")) - }) + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + + It(fmt.Sprintf("should expose the endpoint on localhost (podman=%v, manual=%v, customPortForwarding=%v)", podman, manual, customPortForwarding), func() { + url := fmt.Sprintf("http://%s", ports[ContainerPort]) + if customPortForwarding { + Expect(url).To(ContainSubstring(LocalPort)) } + resp, err := http.Get(url) + Expect(err).ToNot(HaveOccurred()) + defer resp.Body.Close() - By("exposing the endpoint", func() { - Eventually(func(g Gomega) { - url := fmt.Sprintf("http://%s", ports["3000"]) - resp, err := http.Get(url) - g.Expect(err).ToNot(HaveOccurred()) - defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + helper.MatchAllInOutput(string(body), []string{"Hello from Node.js Starter Application!"}) + Expect(err).ToNot(HaveOccurred()) + }) - body, _ := io.ReadAll(resp.Body) - for _, i := range []string{"Hello from Node.js Starter Application!"} { - g.Expect(string(body)).To(ContainSubstring(i)) + When("modifying memoryLimit for container in Devfile", func() { + var stdout string + var stderr string + BeforeEach(func() { + src := "memoryLimit: 1024Mi" + dst := "memoryLimit: 1023Mi" + helper.ReplaceString("devfile.yaml", src, dst) + if manual { + if os.Getenv("SKIP_KEY_PRESS") == "true" { + Skip("This is a unix-terminal specific scenario, skipping") } - g.Expect(err).ToNot(HaveOccurred()) - }).WithPolling(1 * time.Second).WithTimeout(20 * time.Second).Should(Succeed()) + + devSession.PressKey('p') + } + var err error + var stdoutBytes []byte + var stderrBytes []byte + stdoutBytes, stderrBytes, ports, err = devSession.WaitSync() + Expect(err).Should(Succeed()) + stdout = string(stdoutBytes) + stderr = string(stderrBytes) + }) + + It(fmt.Sprintf("should react on the Devfile modification (podman=%v, manual=%v, customPortForwarding=%v)", podman, manual, customPortForwarding), func() { + if podman { + By("warning users that odo dev needs to be restarted", func() { + Expect(stdout).To(ContainSubstring( + "Detected changes in the Devfile, but this is not supported yet on Podman. Please restart 'odo dev' for such changes to be applied.")) + }) + } else { + By("not warning users that odo dev needs to be restarted", func() { + warning := "Please restart 'odo dev'" + Expect(stdout).ShouldNot(ContainSubstring(warning)) + Expect(stderr).ShouldNot(ContainSubstring(warning)) + }) + By("updating the pod", func() { + podName := commonVar.CliRunner.GetRunningPodNameByComponent(cmpName, commonVar.Project) + bufferOutput := commonVar.CliRunner.Run("get", "pods", podName, "-o", "jsonpath='{.spec.containers[0].resources.requests.memory}'").Out.Contents() + output := string(bufferOutput) + Expect(output).To(ContainSubstring("1023Mi")) + }) + + By("exposing the endpoint", func() { + Eventually(func(g Gomega) { + url := fmt.Sprintf("http://%s", ports[ContainerPort]) + if customPortForwarding { + Expect(url).To(ContainSubstring(LocalPort)) + } + resp, err := http.Get(url) + g.Expect(err).ToNot(HaveOccurred()) + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + for _, i := range []string{"Hello from Node.js Starter Application!"} { + g.Expect(string(body)).To(ContainSubstring(i)) + } + g.Expect(err).ToNot(HaveOccurred()) + }).WithPolling(1 * time.Second).WithTimeout(20 * time.Second).Should(Succeed()) + }) + } }) }) }) }) - }) - - When("devfile has multiple endpoints", func() { - BeforeEach(func() { - if !podman { - helper.Cmd("odo", "set", "project", commonVar.Project).ShouldPass() - } - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project-with-multiple-endpoints"), commonVar.Context) - helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-multiple-endpoints.yaml")).ShouldPass() - }) - When("running odo dev", func() { - var devSession helper.DevSession - var ports map[string]string + When("devfile has multiple endpoints", func() { + var ( + LocalPort1 = helper.GetRandomFreePort() + LocalPort2 = helper.GetRandomFreePort() + LocalPort3 = helper.GetRandomFreePort() + ) + const ( + // ContainerPort are hard-coded from devfile-with-multiple-endpoints.yaml + // Note 1: Debug endpoints will not be exposed for this instance, so we do not add custom mapping for them. + // Note 2: We add custom mapping for all the endpoints so that none of them are assigned random ports from the 20001-30001 range; + // Note 2(contd.): this is to avoid a race condition where a test running in parallel is also assigned similar ranged port the one here, and we fail to access either of them. + ContainerPort1 = "3000" + ContainerPort2 = "4567" + ContainerPort3 = "7890" + ) BeforeEach(func() { - opts := []string{} - if manual { - opts = append(opts, "--no-watch") - } - var err error - devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ - CmdlineArgs: opts, - RunOnPodman: podman, - }) - Expect(err).ToNot(HaveOccurred()) + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project-with-multiple-endpoints"), commonVar.Context) + helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-multiple-endpoints.yaml")).ShouldPass() }) - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() - }) - - It("should expose all endpoints on localhost regardless of exposure", func() { - By("not exposing debug endpoints", func() { - for _, p := range []int{5005, 5006} { - _, found := ports[strconv.Itoa(p)] - Expect(found).To(BeFalse(), fmt.Sprintf("debug port %d should not be forwarded", p)) + When("running odo dev", func() { + var devSession helper.DevSession + var ports map[string]string + BeforeEach(func() { + opts := []string{} + if customPortForwarding { + opts = []string{fmt.Sprintf("--port-forward=%s:%s", LocalPort1, ContainerPort1), fmt.Sprintf("--port-forward=%s:%s", LocalPort2, ContainerPort2), fmt.Sprintf("--port-forward=%s:%s", LocalPort3, ContainerPort3)} } + if manual { + opts = append(opts, "--no-watch") + } + var err error + devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ + CmdlineArgs: opts, + NoRandomPorts: NoRandomPorts, + RunOnPodman: podman, + }) + Expect(err).ToNot(HaveOccurred()) }) - getServerResponse := func(p int) (string, error) { - resp, err := http.Get(fmt.Sprintf("http://%s", ports[strconv.Itoa(p)])) - if err != nil { - return "", err - } - defer resp.Body.Close() + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) - body, _ := io.ReadAll(resp.Body) - return string(body), nil - } - containerPorts := []int{3000, 4567, 7890} - for _, p := range containerPorts { - By(fmt.Sprintf("exposing a port targeting container port %d", p), func() { - r, err := getServerResponse(p) - Expect(err).ShouldNot(HaveOccurred()) - helper.MatchAllInOutput(r, []string{"Hello from Node.js Starter Application!"}) + It(fmt.Sprintf("should expose all endpoints on localhost regardless of exposure(podman=%v, manual=%v, customPortForwarding=%v)", podman, manual, customPortForwarding), func() { + By("not exposing debug endpoints", func() { + for _, p := range []int{5005, 5006} { + _, found := ports[strconv.Itoa(p)] + Expect(found).To(BeFalse(), fmt.Sprintf("debug port %d should not be forwarded", p)) + } }) - } - helper.ReplaceString("server.js", "Hello from Node.js", "H3110 from Node.js") + getServerResponse := func(containerPort, localPort string) (string, error) { + url := fmt.Sprintf("http://%s", ports[containerPort]) + if customPortForwarding { + Expect(url).To(ContainSubstring(localPort)) + } + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() - if manual { - if os.Getenv("SKIP_KEY_PRESS") == "true" { - Skip("This is a unix-terminal specific scenario, skipping") + body, _ := io.ReadAll(resp.Body) + return string(body), nil + } + containerPorts := []string{ContainerPort1, ContainerPort2, ContainerPort3} + localPorts := []string{LocalPort1, LocalPort2, LocalPort3} + + for i := range containerPorts { + containerPort := containerPorts[i] + localPort := localPorts[i] + By(fmt.Sprintf("exposing a port targeting container port %s", containerPort), func() { + r, err := getServerResponse(containerPort, localPort) + Expect(err).ShouldNot(HaveOccurred()) + helper.MatchAllInOutput(r, []string{"Hello from Node.js Starter Application!"}) + }) } - devSession.PressKey('p') - } + helper.ReplaceString("server.js", "Hello from Node.js", "H3110 from Node.js") - var stdout, stderr []byte - var err error - stdout, stderr, _, err = devSession.WaitSync() - Expect(err).Should(Succeed()) + if manual { + if os.Getenv("SKIP_KEY_PRESS") == "true" { + Skip("This is a unix-terminal specific scenario, skipping") + } - By("not warning users that odo dev needs to be restarted because the Devfile has not changed", func() { - warning := "Please restart 'odo dev'" - if podman { - warning = "Detected changes in the Devfile, but this is not supported yet on Podman. Please restart 'odo dev' for such changes to be applied." + devSession.PressKey('p') } - Expect(stdout).ShouldNot(ContainSubstring(warning)) - Expect(stderr).ShouldNot(ContainSubstring(warning)) - }) - for _, p := range containerPorts { - By(fmt.Sprintf("returning the right response when querying port forwarded for container port %d", p), - func() { - Eventually(func(g Gomega) string { - r, err := getServerResponse(p) - g.Expect(err).ShouldNot(HaveOccurred()) - return r - }, 180, 10).Should(Equal("H3110 from Node.js Starter Application!")) - }) - } + var stdout, stderr []byte + var err error + stdout, stderr, _, err = devSession.WaitSync() + Expect(err).Should(Succeed()) + + By("not warning users that odo dev needs to be restarted because the Devfile has not changed", func() { + warning := "Please restart 'odo dev'" + if podman { + warning = "Detected changes in the Devfile, but this is not supported yet on Podman. Please restart 'odo dev' for such changes to be applied." + } + Expect(stdout).ShouldNot(ContainSubstring(warning)) + Expect(stderr).ShouldNot(ContainSubstring(warning)) + }) + + for i := range containerPorts { + containerPort := containerPorts[i] + localPort := localPorts[i] + By(fmt.Sprintf("returning the right response when querying port forwarded for container port %s", containerPort), + func() { + Eventually(func(g Gomega) string { + r, err := getServerResponse(containerPort, localPort) + g.Expect(err).ShouldNot(HaveOccurred()) + return r + }, 180, 10).Should(Equal("H3110 from Node.js Starter Application!")) + }) + } + }) }) + }) - }) - })...) - } + } + } + })) } for _, devfileHandlerCtx := range []struct {