@@ -11,94 +11,100 @@ import (
1111 "github.com/stackrox/roxie/internal/deployer"
1212 "github.com/stackrox/roxie/internal/env"
1313 "github.com/stackrox/roxie/internal/logger"
14+ "github.com/stackrox/roxie/internal/roxieenv"
1415 "github.com/stackrox/roxie/internal/types"
1516)
1617
17- func spawnSubshell (d * deployer.Deployer , log * logger.Logger ) error {
18- shellPath := shell
19- if shellPath == "" {
20- shellPath = os .Getenv ("ROXIE_USER_SHELL" )
21- }
22- if shellPath == "" {
23- shellPath = os .Getenv ("SHELL" )
24- }
25- if shellPath == "" {
26- shellPath = "/bin/bash"
27- }
28-
29- log .Infof ("Spawning sub-shell: %s" , shellPath )
30-
31- env := os .Environ ()
32-
33- centralDeploymentInfo := d .GetCentralDeploymentInfo ()
34-
35- if centralDeploymentInfo .Endpoint != "" {
36- env = append (env , fmt .Sprintf ("API_ENDPOINT=%s" , centralDeploymentInfo .Endpoint ))
37- env = append (env , fmt .Sprintf ("ROX_ENDPOINT=%s" , centralDeploymentInfo .Endpoint ))
38- env = append (env , fmt .Sprintf ("ROX_BASE_URL=https://%s" , centralDeploymentInfo .Endpoint ))
39- }
40-
41- if centralDeploymentInfo .Password != "" {
42- env = append (env , fmt .Sprintf ("ROX_ADMIN_PASSWORD=%s" , centralDeploymentInfo .Password ))
43- }
18+ // spawnSubshellForDeployerEnv assembles the roxie environment from a Deployer and invokes an interactive subshell.
19+ func spawnSubshellForDeployerEnv (d * deployer.Deployer , log * logger.Logger ) error {
20+ return runCommandOrSubshell (d .GetCentralDeploymentInfo (), log , nil )
21+ }
4422
45- if centralDeploymentInfo .CACertFile != "" {
46- env = append (env , fmt .Sprintf ("ROX_CA_CERT_FILE=%s" , centralDeploymentInfo .CACertFile ))
23+ // runCommandOrSubshell spawns an interactive subshell or runs the provided command using the given
24+ // central deployment info.
25+ // It handles HAProxy setup, prints the connection banner, and manages shell lifecycle.
26+ func runCommandOrSubshell (centralDeploymentInfo types.CentralDeploymentInfo , log * logger.Logger , args []string ) error {
27+ cmdEnv := os .Environ ()
28+ for name , val := range roxieenv .AssembleRoxieEnvironment (centralDeploymentInfo ).Export () {
29+ cmdEnv = append (cmdEnv , fmt .Sprintf ("%s=%s" , name , val ))
4730 }
48-
49- env = append (env , fmt .Sprintf ("ROX_USERNAME=%s" , deployer .AdminUsername ))
50- env = append (env , "ROXIE_SHELL=1" )
51- env = append (env , fmt .Sprintf ("name=acs@%s" , centralDeploymentInfo .KubeContext ))
31+ cmdEnv = append (cmdEnv , "ROXIE_SHELL=1" )
32+ cmdEnv = append (cmdEnv , fmt .Sprintf ("name=acs@%s" , centralDeploymentInfo .KubeContext ))
5233
5334 haproxyAvailable := isHAProxyAvailable ()
5435
55- var haproxyCmd * exec.Cmd
56- var haproxyConfigPath string
57-
5836 if haproxyAvailable && centralDeploymentInfo .Endpoint != "" && centralDeploymentInfo .CACertFile != "" {
59- var err error
60- haproxyCmd , haproxyConfigPath , err = startHAProxy (centralDeploymentInfo .Endpoint , centralDeploymentInfo .CACertFile , log )
37+ haproxyCmd , haproxyConfigPath , err := startHAProxy (centralDeploymentInfo .Endpoint , centralDeploymentInfo .CACertFile , log )
6138 if err != nil {
6239 log .Warningf ("Failed to start HAProxy: %v" , err )
6340 } else {
64- env = append (env , fmt . Sprintf ( "ROXIE_HAPROXY_CFG_FILE=%s" , haproxyConfigPath ) )
41+ cmdEnv = append (cmdEnv , "ROXIE_HAPROXY_CFG_FILE=" + haproxyConfigPath )
6542 centralDeploymentInfo .HAProxyStarted = true
6643 defer cleanupHAProxy (haproxyCmd , haproxyConfigPath )
6744 }
6845 }
6946
70- printBanner (centralDeploymentInfo )
71-
72- shellCmd := exec .Command (shellPath , "-i" )
73- shellCmd .Env = env
74- shellCmd .Stdin = os .Stdin
75- shellCmd .Stdout = os .Stdout
76- shellCmd .Stderr = os .Stderr
77-
78- err := shellCmd .Run ()
47+ var cmd * exec.Cmd
7948
80- // Print exit message
81- cyan := color .New (color .FgCyan , color .Bold )
82- cyan .Println ("\n [roxie] Exited subshell. You are now back in your original shell." )
83- cyan .Println ("" )
84-
85- // Don't treat shell exit as an error - shells can exit with non-zero status
86- // for various reasons (like the last command failing) which is normal behavior
87- if err != nil {
88- // Check if it's a normal exit (exit code from the shell)
89- if exitErr , ok := err .(* exec.ExitError ); ok {
90- // Shell exited (could be normal exit or last command failed)
91- // This is not an error condition for roxie - the subshell worked fine
92- _ = exitErr // Acknowledge we handled this
93- return nil
49+ if subShellMode (args ) {
50+ shellPath := resolveShellPath ()
51+ log .Infof ("Spawning sub-shell: %s" , shellPath )
52+ printBanner (centralDeploymentInfo )
53+ cmd = exec .Command (shellPath , "-i" )
54+ } else {
55+ // args is non-empty.
56+ cmd = exec .Command (args [0 ], args [1 :]... )
57+ }
58+ cmd .Env = cmdEnv
59+ cmd .Stdin = os .Stdin
60+ cmd .Stdout = os .Stdout
61+ cmd .Stderr = os .Stderr
62+
63+ err := cmd .Run ()
64+
65+ if subShellMode (args ) {
66+ cyan := color .New (color .FgCyan , color .Bold )
67+ cyan .Println ("" )
68+ cyan .Println ("[roxie] Exited subshell. You are now back in your original shell." )
69+ cyan .Println ("[roxie] If you accidentally closed the roxie subshell, you can use `roxie shell` to re-open it." )
70+ cyan .Println ("" )
71+
72+ // Don't treat shell exit as an error - shells can exit with non-zero status
73+ // for various reasons (like the last command failing) which is normal behavior
74+ if err != nil {
75+ // Check if it's a normal exit (exit code from the shell)
76+ if _ , ok := err .(* exec.ExitError ); ok {
77+ return nil
78+ }
79+ // Only return error if we couldn't even start the shell
80+ return fmt .Errorf ("failed to run subshell: %w" , err )
81+ }
82+ } else {
83+ if err != nil {
84+ return fmt .Errorf ("failed to execute command: %w" , err )
9485 }
95- // Only return error if we couldn't even start the shell
96- return fmt .Errorf ("failed to run subshell: %w" , err )
9786 }
9887
9988 return nil
10089}
10190
91+ func subShellMode (args []string ) bool {
92+ return len (args ) == 0
93+ }
94+
95+ func resolveShellPath () string {
96+ if shell != "" {
97+ return shell
98+ }
99+ if s := os .Getenv ("ROXIE_USER_SHELL" ); s != "" {
100+ return s
101+ }
102+ if s := os .Getenv ("SHELL" ); s != "" {
103+ return s
104+ }
105+ return "/bin/bash"
106+ }
107+
102108func startHAProxy (endpoint , caCertFile string , log * logger.Logger ) (* exec.Cmd , string , error ) {
103109 configFile , err := os .CreateTemp ("" , "roxie-haproxy-*.cfg" )
104110 if err != nil {
0 commit comments