This is an automated email from the ASF dual-hosted git repository.

treblereel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git


The following commit(s) were added to refs/heads/main by this push:
     new be87d4b6bd7 kie-issues#1647 [kn-plugin-workflow] Executing kn workflow 
run creates the container in the background (#2778)
be87d4b6bd7 is described below

commit be87d4b6bd7844cf175f74dd25ffc3bca4d19c59
Author: Dmitrii Tikhomirov <trebler...@users.noreply.github.com>
AuthorDate: Wed Jan 8 09:40:59 2025 -0800

    kie-issues#1647 [kn-plugin-workflow] Executing kn workflow run creates the 
container in the background (#2778)
---
 .../kn-plugin-workflow/e2e-tests/helper_test.go    | 71 ++++++++++++++++++++++
 packages/kn-plugin-workflow/e2e-tests/run_test.go  | 18 +++++-
 packages/kn-plugin-workflow/pkg/command/run.go     | 47 +++++++++++++-
 .../kn-plugin-workflow/pkg/common/containers.go    | 44 ++++++++++++++
 4 files changed, 176 insertions(+), 4 deletions(-)

diff --git a/packages/kn-plugin-workflow/e2e-tests/helper_test.go 
b/packages/kn-plugin-workflow/e2e-tests/helper_test.go
index 392e959ee92..ad7f283e820 100644
--- a/packages/kn-plugin-workflow/e2e-tests/helper_test.go
+++ b/packages/kn-plugin-workflow/e2e-tests/helper_test.go
@@ -22,6 +22,7 @@
 package e2e_tests
 
 import (
+       "bufio"
        "bytes"
        "fmt"
        "io"
@@ -32,6 +33,7 @@ import (
        "syscall"
        "testing"
 
+       
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/command"
        
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/command/quarkus"
        "github.com/spf13/cobra"
        "github.com/stretchr/testify/require"
@@ -63,6 +65,11 @@ func ExecuteKnWorkflowWithCmd(cmd *exec.Cmd, args ...string) 
(string, error) {
        return executeCommandWithOutput(cmd, args...)
 }
 
+// ExecuteKnWorkflowWithCmdAndStopContainer executes the 'kn-workflow' CLI 
tool with the given arguments using the provided command and returns the 
containerID and possible error message.
+func ExecuteKnWorkflowWithCmdAndStopContainer(cmd *exec.Cmd, args ...string) 
(string, error) {
+       return executeCommandWithOutputAndStopContainer(cmd, args...)
+}
+
 // ExecuteKnWorkflowQuarkusWithCmd executes the 'kn-workflow' CLI tool with 
'quarkus' command with the given arguments using the provided command and 
returns the command's output and possible error message.
 func ExecuteKnWorkflowQuarkusWithCmd(cmd *exec.Cmd, args ...string) (string, 
error) {
        newArgs := append([]string{"quarkus"}, args...)
@@ -89,6 +96,70 @@ func executeCommandWithOutput(cmd *exec.Cmd, args ...string) 
(string, error) {
        return stdout.String(), nil
 }
 
+func executeCommandWithOutputAndStopContainer(cmd *exec.Cmd, args ...string) 
(string, error) {
+       cmd.Args = append([]string{cmd.Path}, args...)
+
+       var containerId string
+       var stderr bytes.Buffer
+
+       stdoutPipe, err := cmd.StdoutPipe()
+       if err != nil {
+               return "", fmt.Errorf("failed to create stdout pipe: %w", err)
+       }
+       defer stdoutPipe.Close()
+
+       stdinPipe, err := cmd.StdinPipe()
+       if err != nil {
+               return "", fmt.Errorf("failed to create stdin pipe: %w", err)
+       }
+       defer stdinPipe.Close()
+
+       cmd.Stderr = &stderr
+       errorCh := make(chan error, 1)
+
+       go func() {
+               defer close(errorCh)
+               scanner := bufio.NewScanner(stdoutPipe)
+               for scanner.Scan() {
+                       line := scanner.Text()
+
+                       if strings.HasPrefix(line, "Created container with ID 
") {
+                               id, ok := strings.CutPrefix(line, "Created 
container with ID ")
+                               if !ok || id == "" {
+                                       errorCh <- fmt.Errorf("failed to parse 
container ID from output: %q", line)
+                                       return
+                               }
+                               containerId = id
+                       }
+
+                       if line == command.StopContainerMsg {
+                               _, err := io.WriteString(stdinPipe, "any\n")
+                               if err != nil {
+                                       errorCh <- fmt.Errorf("failed to write 
to stdin: %w", err)
+                                       return
+                               }
+                       }
+               }
+
+               if err := scanner.Err(); err != nil {
+                       errorCh <- fmt.Errorf("error reading from stdout: %w", 
err)
+                       return
+               }
+       }()
+
+       err = cmd.Run()
+       if err != nil {
+               return "", fmt.Errorf("command run error: %w (stderr: %s)", 
err, stderr.String())
+       }
+
+       readErr := <-errorCh
+       if readErr != nil {
+               return "", readErr
+       }
+
+       return containerId, nil
+}
+
 // VerifyFileContent verifies that the content of a file matches the expected 
content.
 func VerifyFileContent(t *testing.T, filePath string, expected string) {
        actual, err := os.ReadFile(filePath)
diff --git a/packages/kn-plugin-workflow/e2e-tests/run_test.go 
b/packages/kn-plugin-workflow/e2e-tests/run_test.go
index f3f371b4b21..2cabd4d35e7 100644
--- a/packages/kn-plugin-workflow/e2e-tests/run_test.go
+++ b/packages/kn-plugin-workflow/e2e-tests/run_test.go
@@ -32,6 +32,7 @@ import (
 
        
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/command"
        
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common"
+       "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
 )
 
@@ -80,6 +81,7 @@ func TestRunCommand(t *testing.T) {
 
 func RunRunTest(t *testing.T, cfgTestInputPrepareCreate CfgTestInputCreate, 
test cfgTestInputRun) string {
        var err error
+       var containerId string
 
        // Create the project
        RunCreateTest(t, cfgTestInputPrepareCreate)
@@ -99,7 +101,8 @@ func RunRunTest(t *testing.T, cfgTestInputPrepareCreate 
CfgTestInputCreate, test
        // Run the `run` command
        go func() {
                defer wg.Done()
-               _, err = ExecuteKnWorkflowWithCmd(cmd, 
transformRunCmdCfgToArgs(test.input)...)
+               containerId, err = 
ExecuteKnWorkflowWithCmdAndStopContainer(cmd, 
transformRunCmdCfgToArgs(test.input)...)
+               assert.NotNil(t, containerId, "Container ID is nil")
                require.Truef(t, err == nil || IsSignalInterrupt(err), 
"Expected nil error or signal interrupt, got %v", err)
        }()
 
@@ -120,5 +123,18 @@ func RunRunTest(t *testing.T, cfgTestInputPrepareCreate 
CfgTestInputCreate, test
 
        wg.Wait()
 
+       stopped := make(chan bool)
+       t.Logf("Checking if container is stopped")
+       assert.NotNil(t, containerId, "Container ID is nil")
+       // Check if the container is stopped within a specified time limit.
+       go common.PollContainerStoppedCheck(containerId, pollInterval, stopped)
+       select {
+       case <-stopped:
+               fmt.Println("Project is stopped")
+       case <-time.After(timeout):
+               t.Fatalf("Test case timed out after %s. The project was not 
stopped within the specified time.", timeout)
+               cmd.Process.Signal(os.Interrupt)
+       }
+
        return projectName
 }
diff --git a/packages/kn-plugin-workflow/pkg/command/run.go 
b/packages/kn-plugin-workflow/pkg/command/run.go
index 27ed3ae5e1c..7cb275c632c 100644
--- a/packages/kn-plugin-workflow/pkg/command/run.go
+++ b/packages/kn-plugin-workflow/pkg/command/run.go
@@ -20,6 +20,7 @@
 package command
 
 import (
+       "bufio"
        "fmt"
        "os"
        "sync"
@@ -34,8 +35,12 @@ import (
 type RunCmdConfig struct {
        PortMapping string
        OpenDevUI   bool
+       StopContainerOnUserInput bool
 }
 
+const StopContainerMsg = "Press any key to stop the container"
+
+
 func NewRunCommand() *cobra.Command {
        cmd := &cobra.Command{
                Use:   "run",
@@ -56,9 +61,13 @@ func NewRunCommand() *cobra.Command {
 
        # Disable automatic browser launch of SonataFlow  Dev UI
        {{.Name}} run --open-dev-ui=false
+
+       # Stop the container when the user presses any key
+       {{.Name}} run --stop-container-on-user-input=false
+
                 `,
                SuggestFor: []string{"rnu", "start"}, //nolint:misspell
-               PreRunE:    common.BindEnv("port", "open-dev-ui"),
+               PreRunE:    common.BindEnv("port", "open-dev-ui", 
"stop-container-on-user-input"),
        }
 
        cmd.RunE = func(cmd *cobra.Command, args []string) error {
@@ -67,6 +76,7 @@ func NewRunCommand() *cobra.Command {
 
        cmd.Flags().StringP("port", "p", "8080", "Maps a different host port to 
the running container port.")
        cmd.Flags().Bool("open-dev-ui", true, "Disable automatic browser launch 
of SonataFlow  Dev UI")
+       cmd.Flags().Bool("stop-container-on-user-input", true, "Stop the 
container when the user presses any key")
        cmd.SetHelpFunc(common.DefaultTemplatedHelp)
 
        return cmd
@@ -92,8 +102,9 @@ func run() error {
 
 func runDevCmdConfig() (cfg RunCmdConfig, err error) {
        cfg = RunCmdConfig{
-               PortMapping: viper.GetString("port"),
-               OpenDevUI:   viper.GetBool("open-dev-ui"),
+               PortMapping:                            viper.GetString("port"),
+               OpenDevUI:                              
viper.GetBool("open-dev-ui"),
+               StopContainerOnUserInput:       
viper.GetBool("stop-container-on-user-input"),
        }
        return
 }
@@ -137,6 +148,36 @@ func runSWFProjectDevMode(containerTool string, cfg 
RunCmdConfig) (err error) {
        pollInterval := 5 * time.Second
        common.ReadyCheck(readyCheckURL, pollInterval, cfg.PortMapping, 
cfg.OpenDevUI)
 
+       if cfg.StopContainerOnUserInput {
+               if err := stopContainer(containerTool); err != nil {
+                       return err
+               }
+       }
+
        wg.Wait()
        return err
 }
+
+func stopContainer(containerTool string) error {
+       fmt.Println(StopContainerMsg)
+
+       reader := bufio.NewReader(os.Stdin)
+
+       _, err := reader.ReadString('\n')
+       if err != nil {
+               return fmt.Errorf("error reading from stdin: %w", err)
+       }
+
+       fmt.Println("⏳ Stopping the container...")
+
+       containerID, err := common.GetContainerID(containerTool)
+       if err != nil {
+               return err
+       }
+       if err := common.StopContainer(containerTool, containerID); err != nil {
+               return err
+       }
+       return nil
+}
+
+
diff --git a/packages/kn-plugin-workflow/pkg/common/containers.go 
b/packages/kn-plugin-workflow/pkg/common/containers.go
index afb2482b69e..44d9e3ab883 100644
--- a/packages/kn-plugin-workflow/pkg/common/containers.go
+++ b/packages/kn-plugin-workflow/pkg/common/containers.go
@@ -389,3 +389,47 @@ func processOutputDuringContainerExecution(cli 
*client.Client, ctx context.Conte
 
        return nil
 }
+
+
+func PollContainerStoppedCheck(containerID string, interval time.Duration, 
ready chan<- bool) {
+       for {
+               running, err := IsContainerRunning(containerID)
+               if err != nil {
+                       fmt.Printf("Error checking if container %s is running: 
%s", containerID, err)
+                       ready <- false
+                       return
+               }
+               if !running {
+                       ready <- true
+                       return
+               }
+               time.Sleep(interval)
+       }
+}
+
+func IsContainerRunning(containerID string) (bool, error) {
+       if errDocker := CheckDocker(); errDocker == nil {
+               cli, err := getDockerClient()
+               if err != nil {
+                       return false, fmt.Errorf("unable to create docker 
client: %w", err)
+               }
+               containerJSON, err := 
cli.ContainerInspect(context.Background(), containerID)
+               if err != nil {
+                       if client.IsErrNotFound(err) {
+                               return false, nil
+                       }
+                       return false, fmt.Errorf("unable to inspect container 
%s with docker: %w", containerID, err)
+               }
+               return containerJSON.State.Running, nil
+
+       } else if errPodman := CheckPodman(); errPodman == nil {
+               cmd := exec.Command("podman", "inspect", containerID, 
"--format", "{{.State.Running}}")
+               output, err := cmd.Output()
+               if err != nil {
+                       return false, fmt.Errorf("unable to inspect container 
%s with podman: %w", containerID, err)
+               }
+               return strings.TrimSpace(string(output)) == "true", nil
+       }
+
+       return false, fmt.Errorf("there is no docker or podman available")
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@kie.apache.org
For additional commands, e-mail: commits-h...@kie.apache.org

Reply via email to