Skip to content

Commit 5a7771b

Browse files
authored
Update Wsh Command (#1715)
This adds an RPC command for updating wsh on a remote machine without starting a new session. It is not being used yet, but will be used for connections using a single server in the future.
1 parent b26d233 commit 5a7771b

8 files changed

Lines changed: 138 additions & 3 deletions

File tree

cmd/wsh/cmd/wshcmd-connserver.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os"
1313
"path/filepath"
1414
"sync/atomic"
15+
"syscall"
1516
"time"
1617

1718
"github.com/spf13/cobra"
@@ -33,9 +34,11 @@ var serverCmd = &cobra.Command{
3334
}
3435

3536
var connServerRouter bool
37+
var singleServerRouter bool
3638

3739
func init() {
3840
serverCmd.Flags().BoolVar(&connServerRouter, "router", false, "run in local router mode")
41+
serverCmd.Flags().BoolVar(&singleServerRouter, "single", false, "run in local single mode")
3942
rootCmd.AddCommand(serverCmd)
4043
}
4144

@@ -186,6 +189,39 @@ func serverRunRouter() error {
186189
select {}
187190
}
188191

192+
func checkForUpdate() error {
193+
remoteInfo := wshutil.GetInfo(RpcContext)
194+
needsRestartRaw, err := RpcClient.SendRpcRequest(wshrpc.Command_ConnUpdateWsh, remoteInfo, &wshrpc.RpcOpts{Timeout: 60000})
195+
if err != nil {
196+
return fmt.Errorf("could not update: %w", err)
197+
}
198+
needsRestart, ok := needsRestartRaw.(bool)
199+
if !ok {
200+
return fmt.Errorf("wrong return type from update")
201+
}
202+
if needsRestart {
203+
// run the restart command here
204+
// how to get the correct path?
205+
return syscall.Exec("~/.waveterm/bin/wsh", []string{"wsh", "connserver", "--single"}, []string{})
206+
}
207+
return nil
208+
}
209+
210+
func serverRunSingle() error {
211+
err := setupRpcClient(&wshremote.ServerImpl{LogWriter: os.Stdout})
212+
if err != nil {
213+
return err
214+
}
215+
WriteStdout("running wsh connserver (%s)\n", RpcContext.Conn)
216+
err = checkForUpdate()
217+
if err != nil {
218+
return err
219+
}
220+
221+
go wshremote.RunSysInfoLoop(RpcClient, RpcContext.Conn)
222+
select {} // run forever
223+
}
224+
189225
func serverRunNormal() error {
190226
err := setupRpcClient(&wshremote.ServerImpl{LogWriter: os.Stdout})
191227
if err != nil {
@@ -197,7 +233,9 @@ func serverRunNormal() error {
197233
}
198234

199235
func serverRun(cmd *cobra.Command, args []string) error {
200-
if connServerRouter {
236+
if singleServerRouter {
237+
return serverRunSingle()
238+
} else if connServerRouter {
201239
return serverRunRouter()
202240
} else {
203241
return serverRunNormal()

frontend/app/store/wshclientapi.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class RpcApiType {
5757
return client.wshRpcCall("connstatus", null, opts);
5858
}
5959

60+
// command "connupdatewsh" [call]
61+
ConnUpdateWshCommand(client: WshClient, data: RemoteInfo, opts?: RpcOpts): Promise<boolean> {
62+
return client.wshRpcCall("connupdatewsh", data, opts);
63+
}
64+
6065
// command "controllerappendoutput" [call]
6166
ControllerAppendOutputCommand(client: WshClient, data: CommandControllerAppendOutputData, opts?: RpcOpts): Promise<void> {
6267
return client.wshRpcCall("controllerappendoutput", data, opts);

frontend/types/gotypes.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,15 @@ declare global {
553553
y: number;
554554
};
555555

556+
// wshrpc.RemoteInfo
557+
type RemoteInfo = {
558+
host: string;
559+
clientarch: string;
560+
clientos: string;
561+
clientversion: string;
562+
shell: string;
563+
};
564+
556565
// wshutil.RpcMessage
557566
type RpcMessage = {
558567
command?: string;

pkg/remote/conncontroller/conncontroller.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func (conn *SSHConn) OpenDomainSocketListener(ctx context.Context) error {
228228
// expects the output of `wsh version` which looks like `wsh v0.10.4` or "not-installed"
229229
// returns (up-to-date, semver, error)
230230
// if not up to date, or error, version might be ""
231-
func isWshVersionUpToDate(wshVersionLine string) (bool, string, error) {
231+
func IsWshVersionUpToDate(wshVersionLine string) (bool, string, error) {
232232
wshVersionLine = strings.TrimSpace(wshVersionLine)
233233
if wshVersionLine == "not-installed" {
234234
return false, "", nil
@@ -290,7 +290,7 @@ func (conn *SSHConn) StartConnServer(ctx context.Context) (bool, string, error)
290290
return false, "", fmt.Errorf("error reading wsh version: %w", err)
291291
}
292292
conn.Infof(ctx, "got connserver version: %s\n", strings.TrimSpace(versionLine))
293-
isUpToDate, clientVersion, err := isWshVersionUpToDate(versionLine)
293+
isUpToDate, clientVersion, err := IsWshVersionUpToDate(versionLine)
294294
if err != nil {
295295
sshSession.Close()
296296
return false, "", fmt.Errorf("error checking wsh version: %w", err)
@@ -377,6 +377,22 @@ to ensure a seamless experience.
377377
Would you like to install them?
378378
`)
379379

380+
func (conn *SSHConn) UpdateWsh(ctx context.Context, clientDisplayName string, remoteInfo *wshrpc.RemoteInfo) error {
381+
conn.Infof(ctx, "attempting to update wsh for connection %s (os:%s arch:%s version:%s)\n",
382+
remoteInfo.ConnName, remoteInfo.ClientOs, remoteInfo.ClientArch, remoteInfo.ClientVersion)
383+
client := conn.GetClient()
384+
if client == nil {
385+
return fmt.Errorf("cannot update wsh: ssh client is not connected")
386+
}
387+
err := remote.CpWshToRemote(ctx, client, remoteInfo.ClientOs, remoteInfo.ClientArch)
388+
if err != nil {
389+
return fmt.Errorf("error installing wsh to remote: %w", err)
390+
}
391+
conn.Infof(ctx, "successfully updated wsh on %s\n", conn.GetName())
392+
return nil
393+
394+
}
395+
380396
// returns (allowed, error)
381397
func (conn *SSHConn) getPermissionToInstallWsh(ctx context.Context, clientDisplayName string) (bool, error) {
382398
conn.Infof(ctx, "running getPermissionToInstallWsh...\n")

pkg/wshrpc/wshclient/wshclient.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ func ConnStatusCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]wshrpc.ConnSt
7373
return resp, err
7474
}
7575

76+
// command "connupdatewsh", wshserver.ConnUpdateWshCommand
77+
func ConnUpdateWshCommand(w *wshutil.WshRpc, data wshrpc.RemoteInfo, opts *wshrpc.RpcOpts) (bool, error) {
78+
resp, err := sendRpcRequestCallHelper[bool](w, "connupdatewsh", data, opts)
79+
return resp, err
80+
}
81+
7682
// command "controllerappendoutput", wshserver.ControllerAppendOutputCommand
7783
func ControllerAppendOutputCommand(w *wshutil.WshRpc, data wshrpc.CommandControllerAppendOutputData, opts *wshrpc.RpcOpts) error {
7884
_, err := sendRpcRequestCallHelper[any](w, "controllerappendoutput", data, opts)

pkg/wshrpc/wshrpctypes.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const (
8484
Command_WslList = "wsllist"
8585
Command_WslDefaultDistro = "wsldefaultdistro"
8686
Command_DismissWshFail = "dismisswshfail"
87+
Command_ConnUpdateWsh = "updatewsh"
8788

8889
Command_WorkspaceList = "workspacelist"
8990

@@ -163,6 +164,7 @@ type WshRpcInterface interface {
163164
WslListCommand(ctx context.Context) ([]string, error)
164165
WslDefaultDistroCommand(ctx context.Context) (string, error)
165166
DismissWshFailCommand(ctx context.Context, connName string) error
167+
ConnUpdateWshCommand(ctx context.Context, remoteInfo RemoteInfo) (bool, error)
166168

167169
// eventrecv is special, it's handled internally by WshRpc with EventListener
168170
EventRecvCommand(ctx context.Context, data wps.WaveEvent) error
@@ -500,6 +502,14 @@ type ConnRequest struct {
500502
LogBlockId string `json:"logblockid,omitempty"`
501503
}
502504

505+
type RemoteInfo struct {
506+
ConnName string `json:"host"`
507+
ClientArch string `json:"clientarch"`
508+
ClientOs string `json:"clientos"`
509+
ClientVersion string `json:"clientversion"`
510+
Shell string `json:"shell"`
511+
}
512+
503513
const (
504514
TimeSeries_Cpu = "cpu"
505515
)

pkg/wshrpc/wshserver/wshserver.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,45 @@ func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, data wshrpc.Co
699699
return conn.InstallWsh(ctx)
700700
}
701701

702+
func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, remoteInfo wshrpc.RemoteInfo) (bool, error) {
703+
connName := remoteInfo.ConnName
704+
if connName == "" {
705+
return false, fmt.Errorf("invalid remote info: missing connection name")
706+
}
707+
708+
log.Printf("checking wsh version for connection %s (current: %s)", connName, remoteInfo.ClientVersion)
709+
upToDate, _, err := conncontroller.IsWshVersionUpToDate(remoteInfo.ClientVersion)
710+
if err != nil {
711+
return false, fmt.Errorf("unable to compare wsh version: %w", err)
712+
}
713+
if upToDate {
714+
// no need to update
715+
log.Printf("wsh is already up to date for connection %s", connName)
716+
return false, nil
717+
}
718+
719+
// todo: need to add user input code here for validation
720+
721+
if strings.HasPrefix(connName, "wsl://") {
722+
return false, fmt.Errorf("connupdatewshcommand is not supported for wsl connections")
723+
}
724+
connOpts, err := remote.ParseOpts(connName)
725+
if err != nil {
726+
return false, fmt.Errorf("error parsing connection name: %w", err)
727+
}
728+
conn := conncontroller.GetConn(ctx, connOpts, false, &wshrpc.ConnKeywords{})
729+
if conn == nil {
730+
return false, fmt.Errorf("connection not found: %s", connName)
731+
}
732+
err = conn.UpdateWsh(ctx, connName, &remoteInfo)
733+
if err != nil {
734+
return false, fmt.Errorf("wsh update failed for connection %s: %w", connName, err)
735+
}
736+
737+
// todo: need to add code for modifying configs?
738+
return true, nil
739+
}
740+
702741
func (ws *WshServer) ConnListCommand(ctx context.Context) ([]string, error) {
703742
return conncontroller.GetConnectionsList()
704743
}

pkg/wshutil/wshutil.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"net"
1313
"os"
1414
"os/signal"
15+
"runtime"
1516
"sync"
1617
"sync/atomic"
1718
"syscall"
@@ -536,3 +537,14 @@ func ExtractUnverifiedSocketName(tokenStr string) (string, error) {
536537
sockName = wavebase.ExpandHomeDirSafe(sockName)
537538
return sockName, nil
538539
}
540+
541+
func GetInfo(rpcContext wshrpc.RpcContext) wshrpc.RemoteInfo {
542+
return wshrpc.RemoteInfo{
543+
ConnName: rpcContext.Conn,
544+
ClientArch: runtime.GOARCH,
545+
ClientOs: runtime.GOOS,
546+
ClientVersion: wavebase.WaveVersion,
547+
Shell: os.Getenv("SHELL"),
548+
}
549+
550+
}

0 commit comments

Comments
 (0)