diff --git a/tests/helper/helper_dev.go b/tests/helper/helper_dev.go index 7c077563ae1..2d2abc4062b 100644 --- a/tests/helper/helper_dev.go +++ b/tests/helper/helper_dev.go @@ -1,6 +1,7 @@ package helper import ( + "fmt" "os" "regexp" "time" @@ -112,6 +113,7 @@ type DevSession struct { session *gexec.Session stopped bool console *expect.Console + address string } type DevSessionOpts struct { @@ -120,6 +122,7 @@ type DevSessionOpts struct { RunOnPodman bool TimeoutInSeconds int NoRandomPorts bool + CustomAddress string } // StartDevMode starts a dev session with `odo dev` @@ -139,6 +142,9 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, out []byte, er if !options.NoRandomPorts { args = append(args, "--random-ports") } + if options.CustomAddress != "" { + args = append(args, "--address", options.CustomAddress) + } args = append(args, options.CmdlineArgs...) cmd := Cmd("odo", args...) cmd.Cmd.Stdin = c.Tty() @@ -154,6 +160,7 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, out []byte, er result := DevSession{ session: session, console: c, + address: options.CustomAddress, } outContents := session.Out.Contents() errContents := session.Err.Contents() @@ -165,7 +172,7 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, out []byte, er if err != nil { return DevSession{}, nil, nil, nil, err } - return result, outContents, errContents, getPorts(string(outContents)), nil + return result, outContents, errContents, getPorts(string(outContents), options.CustomAddress), nil } @@ -242,7 +249,7 @@ func (o DevSession) GetInfo() ([]byte, []byte, map[string]string, error) { if err != nil { return nil, nil, nil, err } - return outContents, errContents, getPorts(string(outContents)), nil + return outContents, errContents, getPorts(string(outContents), o.address), nil } func (o DevSession) CheckNotSynced(timeout time.Duration) { @@ -279,6 +286,9 @@ func WaitForDevModeToContain(options DevSessionOpts, substring string, stopSessi if options.RunOnPodman { args = append(args, "--platform", "podman") } + if options.CustomAddress != "" { + args = append(args, "--address", options.CustomAddress) + } session := Cmd("odo", args...).AddEnv(options.EnvVars...).Runner().session if checkErrOut { WaitForErroutToContain(substring, 360, 10, session) @@ -287,6 +297,7 @@ func WaitForDevModeToContain(options DevSessionOpts, substring string, stopSessi } result := DevSession{ session: session, + address: options.CustomAddress, } if stopSessionAfter { defer func() { @@ -311,9 +322,12 @@ func WaitForDevModeToContain(options DevSessionOpts, substring string, stopSessi // getPorts returns a map of ports redirected depending on the information in s // // `- Forwarding from 127.0.0.1:20001 -> 3000` will return { "3000": "127.0.0.1:20001" } -func getPorts(s string) map[string]string { +func getPorts(s, address string) map[string]string { + if address == "" { + address = "127.0.0.1" + } result := map[string]string{} - re := regexp.MustCompile("(127.0.0.1:[0-9]+) -> ([0-9]+)") + re := regexp.MustCompile(fmt.Sprintf("(%s:[0-9]+) -> ([0-9]+)", address)) matches := re.FindAllStringSubmatch(s, -1) for _, match := range matches { result[match[2]] = match[1] diff --git a/tests/integration/cmd_dev_test.go b/tests/integration/cmd_dev_test.go index c41e1cff02c..7ec3c4aca97 100644 --- a/tests/integration/cmd_dev_test.go +++ b/tests/integration/cmd_dev_test.go @@ -13,6 +13,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" . "github.com/onsi/ginkgo/v2" @@ -975,252 +976,275 @@ ComponentSettings: }) }) }) - - for _, customPortForwarding := range []bool{true, false} { - customPortForwarding := customPortForwarding - var NoRandomPorts bool - if customPortForwarding { - NoRandomPorts = true + for _, customAddress := range []bool{true, false} { + customAddress := customAddress + var LocalAddress string + if customAddress { + LocalAddress = "0.0.0.0" } - When("devfile has single endpoint", func() { - var ( - LocalPort int - ) - const ( - ContainerPort = "3000" - ) - BeforeEach(func() { - LocalPort = helper.GetCustomStartPort() - 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 int + ) + const ( + ContainerPort = "3000" + ) BeforeEach(func() { - var err error - opts := []string{} - if customPortForwarding { - opts = []string{fmt.Sprintf("--port-forward=%d:%s", LocalPort, ContainerPort)} - } - if manual { - opts = append(opts, "--no-watch") - } - devSession, _, _, ports, err = helper.StartDevMode(helper.DevSessionOpts{ - CmdlineArgs: opts, - NoRandomPorts: NoRandomPorts, - RunOnPodman: podman, - }) - Expect(err).ToNot(HaveOccurred()) + LocalPort = helper.GetCustomStartPort() + 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(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(strconv.Itoa(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 + 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=%d:%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, + CustomAddress: LocalAddress, + }) + Expect(err).ToNot(HaveOccurred()) }) - 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")) - }) + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) - By("exposing the endpoint", func() { - Eventually(func(g Gomega) { - url := fmt.Sprintf("http://%s", ports[ContainerPort]) - if customPortForwarding { - Expect(url).To(ContainSubstring(strconv.Itoa(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()) - }) + It(fmt.Sprintf("should expose the endpoint on localhost (podman=%v, manual=%v, customPortForwarding=%v, customAddress=%v)", podman, manual, customPortForwarding, customAddress), func() { + url := fmt.Sprintf("http://%s", ports[ContainerPort]) + if customPortForwarding { + Expect(url).To(ContainSubstring(strconv.Itoa(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("devfile has multiple endpoints", func() { - var ( - LocalPort1, LocalPort2, LocalPort3 int - ) - 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() { - LocalPort1 = helper.GetCustomStartPort() - LocalPort2 = LocalPort1 + 1 - LocalPort3 = LocalPort1 + 2 - 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("modifying memoryLimit for container in Devfile", func() { + var stdout string + var stderr string + BeforeEach(func() { + if manual { + if os.Getenv("SKIP_KEY_PRESS") == "true" { + Skip("This is a unix-terminal specific scenario, skipping") + } + } + var ( + wg sync.WaitGroup + err error + stdoutBytes []byte + stderrBytes []byte + ) + wg.Add(1) + go func() { + defer wg.Done() + stdoutBytes, stderrBytes, ports, err = devSession.WaitSync() + Expect(err).Should(Succeed()) + stdout = string(stdoutBytes) + stderr = string(stderrBytes) + }() + src := "memoryLimit: 1024Mi" + dst := "memoryLimit: 1023Mi" + helper.ReplaceString("devfile.yaml", src, dst) + if manual { + devSession.PressKey('p') + } + wg.Wait() + }) - 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=%d:%s", LocalPort1, ContainerPort1), fmt.Sprintf("--port-forward=%d:%s", LocalPort2, ContainerPort2), fmt.Sprintf("--port-forward=%d:%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, + It(fmt.Sprintf("should react on the Devfile modification (podman=%v, manual=%v, customPortForwarding=%v, customAddress=%v)", podman, manual, customPortForwarding, customAddress), 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(strconv.Itoa(LocalPort))) + } + if customAddress { + Expect(url).To(ContainSubstring(LocalAddress)) + } + 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()) + }) + } + }) }) - Expect(err).ToNot(HaveOccurred()) }) + }) - AfterEach(func() { - devSession.Stop() - devSession.WaitEnd() + When("devfile has multiple endpoints", func() { + var ( + LocalPort1, LocalPort2, LocalPort3 int + ) + 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() { + LocalPort1 = helper.GetCustomStartPort() + LocalPort2 = LocalPort1 + 1 + LocalPort3 = LocalPort1 + 2 + 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() }) - 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)) + 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=%d:%s", LocalPort1, ContainerPort1), fmt.Sprintf("--port-forward=%d:%s", LocalPort2, ContainerPort2), fmt.Sprintf("--port-forward=%d:%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, + CustomAddress: LocalAddress, + }) + Expect(err).ToNot(HaveOccurred()) }) - getServerResponse := func(containerPort, localPort string) (string, error) { - url := fmt.Sprintf("http://%s", ports[containerPort]) - if customPortForwarding { - Expect(url).To(ContainSubstring(localPort)) + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + + It(fmt.Sprintf("should expose all endpoints on localhost regardless of exposure(podman=%v, manual=%v, customPortForwarding=%v, customAddress=%v)", podman, manual, customPortForwarding, customAddress), 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]) + if customPortForwarding { + Expect(url).To(ContainSubstring(localPort)) + } + if customAddress { + Expect(url).To(ContainSubstring(LocalAddress)) + } + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + return string(body), nil } - resp, err := http.Get(url) - if err != nil { - return "", err + containerPorts := []string{ContainerPort1, ContainerPort2, ContainerPort3} + localPorts := []int{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, strconv.Itoa(localPort)) + Expect(err).ShouldNot(HaveOccurred()) + helper.MatchAllInOutput(r, []string{"Hello from Node.js Starter Application!"}) + }) } - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) - return string(body), nil - } - containerPorts := []string{ContainerPort1, ContainerPort2, ContainerPort3} - localPorts := []int{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, strconv.Itoa(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") - helper.ReplaceString("server.js", "Hello from Node.js", "H3110 from Node.js") + if manual { + if os.Getenv("SKIP_KEY_PRESS") == "true" { + Skip("This is a unix-terminal specific scenario, skipping") + } - if manual { - if os.Getenv("SKIP_KEY_PRESS") == "true" { - Skip("This is a unix-terminal specific scenario, skipping") + devSession.PressKey('p') } - devSession.PressKey('p') - } + var stdout, stderr []byte + var err error + stdout, stderr, _, err = devSession.WaitSync() + Expect(err).Should(Succeed()) - 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)) + }) - 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." + 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, strconv.Itoa(localPort)) + g.Expect(err).ShouldNot(HaveOccurred()) + return r + }, 180, 10).Should(Equal("H3110 from Node.js Starter Application!")) + }) } - 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, strconv.Itoa(localPort)) - g.Expect(err).ShouldNot(HaveOccurred()) - return r - }, 180, 10).Should(Equal("H3110 from Node.js Starter Application!")) - }) - } }) - }) - }) + }) + } } } }))