@@ -22,6 +22,7 @@ import (
2222 "github.com/loft-sh/devpod/pkg/ts"
2323 "github.com/loft-sh/log"
2424 perrors "github.com/pkg/errors"
25+ "github.com/skratchdot/open-golang/open"
2526 "golang.org/x/crypto/ssh"
2627 "tailscale.com/client/tailscale"
2728 "tailscale.com/tailcfg"
@@ -123,28 +124,55 @@ func (c *client) RefreshOptions(ctx context.Context, userOptionsRaw []string, re
123124 return nil
124125}
125126
126- func (c * client ) SSHClients (ctx context.Context , user string ) ( toolClient * ssh. Client , userClient * ssh. Client , err error ) {
127+ func (c * client ) CheckWorkspaceReachable (ctx context.Context ) error {
127128 wAddr , err := c .getWorkspaceAddress ()
128129 if err != nil {
129- return nil , nil , fmt .Errorf ("resolve workspace hostname: %w" , err )
130+ return fmt .Errorf ("resolve workspace hostname: %w" , err )
130131 }
131-
132- err = ts .WaitHostReachable (ctx , c .tsClient , wAddr , c .log )
132+ err = ts .WaitHostReachable (ctx , c .tsClient , wAddr , 5 , false , c .log )
133133 if err != nil {
134134 instance , getWorkspaceErr := c .localClient .GetWorkspace (ctx , c .workspace .UID )
135+ // if we can't reach the daemon try to start the desktop app
136+ if daemon .IsDaemonNotAvailableError (getWorkspaceErr ) {
137+ deeplink := fmt .Sprintf ("devpod://open?workspace=%s&provider=%s&source=%s&ide=%s" , c .workspace .ID , c .config .Name , c .workspace .Source .String (), c .workspace .IDE .Name )
138+ openErr := open .Run (deeplink )
139+ if openErr != nil {
140+ return getWorkspaceErr // inform user about daemon state
141+ }
142+ // give desktop app a chance to start
143+ time .Sleep (2 * time .Second )
144+
145+ // let's try again
146+ err = ts .WaitHostReachable (ctx , c .tsClient , wAddr , 20 , true , c .log )
147+ if err != nil {
148+ instance , getWorkspaceErr = c .localClient .GetWorkspace (ctx , c .workspace .UID )
149+ } else {
150+ return nil
151+ }
152+ }
153+
135154 if getWorkspaceErr != nil {
136- return nil , nil , fmt .Errorf ("couldn't get workspace: %w" , getWorkspaceErr )
155+ return fmt .Errorf ("couldn't get workspace: %w" , getWorkspaceErr )
137156 } else if instance .Status .Phase != storagev1 .InstanceReady {
138- return nil , nil , fmt .Errorf ("workspace is '%s', please run `devpod up %s` to start it again" , instance .Status .Phase , c .workspace .ID )
157+ return fmt .Errorf ("workspace is '%s', please run `devpod up %s` to start it again" , instance .Status .Phase , c .workspace .ID )
139158 } else if instance .Status .LastWorkspaceStatus != storagev1 .WorkspaceStatusRunning {
140- return nil , nil , fmt .Errorf ("workspace is '%s', please run `devpod up %s` to start it again" , instance .Status .LastWorkspaceStatus , c .workspace .ID )
159+ return fmt .Errorf ("workspace is '%s', please run `devpod up %s` to start it again" , instance .Status .LastWorkspaceStatus , c .workspace .ID )
141160 }
142161
143- return nil , nil , fmt .Errorf ("reach host: %w" , err )
162+ return fmt .Errorf ("reach host: %w" , err )
144163 }
145164
146165 c .log .Debugf ("Host %s is reachable. Proceeding with SSH session..." , wAddr .Host ())
147166
167+ return nil
168+ }
169+
170+ func (c * client ) SSHClients (ctx context.Context , user string ) (toolClient * ssh.Client , userClient * ssh.Client , err error ) {
171+ wAddr , err := c .getWorkspaceAddress ()
172+ if err != nil {
173+ return nil , nil , fmt .Errorf ("resolve workspace hostname: %w" , err )
174+ }
175+
148176 toolClient , err = ts .WaitForSSHClient (ctx , c .tsClient , wAddr .Host (), wAddr .Port (), "root" , c .log )
149177 if err != nil {
150178 return nil , nil , fmt .Errorf ("create SSH tool client: %w" , err )
0 commit comments