From c741dac2c24083e11f9fb196d730cc05f7ed612f Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Thu, 9 Jan 2025 14:27:11 -0800 Subject: [PATCH 1/7] feat: fist pass at the single server update cmd This will theoretically allow updates to happen seamlessly without requiring a separate session. --- cmd/wsh/cmd/wshcmd-connserver.go | 46 ++++++++++++++++++++- frontend/app/store/wshclientapi.ts | 5 +++ frontend/types/gotypes.d.ts | 8 ++++ pkg/remote/conncontroller/conncontroller.go | 22 ++++++++++ pkg/wshrpc/wshclient/wshclient.go | 6 +++ pkg/wshrpc/wshrpctypes.go | 9 ++++ pkg/wshrpc/wshserver/wshserver.go | 31 ++++++++++++++ 7 files changed, 126 insertions(+), 1 deletion(-) diff --git a/cmd/wsh/cmd/wshcmd-connserver.go b/cmd/wsh/cmd/wshcmd-connserver.go index da2e5493f8..cde779dbc4 100644 --- a/cmd/wsh/cmd/wshcmd-connserver.go +++ b/cmd/wsh/cmd/wshcmd-connserver.go @@ -11,7 +11,9 @@ import ( "net" "os" "path/filepath" + "runtime" "sync/atomic" + "syscall" "time" "github.com/spf13/cobra" @@ -33,9 +35,11 @@ var serverCmd = &cobra.Command{ } var connServerRouter bool +var singleServerRouter bool func init() { serverCmd.Flags().BoolVar(&connServerRouter, "router", false, "run in local router mode") + serverCmd.Flags().BoolVar(&singleServerRouter, "single", false, "run in local single mode") rootCmd.AddCommand(serverCmd) } @@ -186,6 +190,44 @@ func serverRunRouter() error { select {} } +func checkForUpdate() error { + updateInfo := wshrpc.UpdateInfo{ + ConnName: RpcContext.Conn, + ClientArch: runtime.GOARCH, + ClientOs: runtime.GOOS, + ClientVersion: wavebase.WaveVersion, + } + needsRestartRaw, err := RpcClient.SendRpcRequest(wshrpc.Command_ConnUpdateWsh, updateInfo, &wshrpc.RpcOpts{Timeout: 2000}) + if err != nil { + return fmt.Errorf("could not update: %w", err) + } + needsRestart, ok := needsRestartRaw.(bool) + if !ok { + return fmt.Errorf("wrong return type from update") + } + if needsRestart { + // run the restart command here + // how to get the correct path? + return syscall.Exec("~/.waveterm/bin/wsh", []string{"wsh", "connserver", "--single"}, []string{}) + } + return nil +} + +func serverRunSingle() error { + err := setupRpcClient(&wshremote.ServerImpl{LogWriter: os.Stdout}) + if err != nil { + return err + } + WriteStdout("running wsh connserver (%s)\n", RpcContext.Conn) + err = checkForUpdate() + if err != nil { + return err + } + + go wshremote.RunSysInfoLoop(RpcClient, RpcContext.Conn) + select {} // run forever +} + func serverRunNormal() error { err := setupRpcClient(&wshremote.ServerImpl{LogWriter: os.Stdout}) if err != nil { @@ -197,7 +239,9 @@ func serverRunNormal() error { } func serverRun(cmd *cobra.Command, args []string) error { - if connServerRouter { + if singleServerRouter { + return serverRunSingle() + } else if connServerRouter { return serverRunRouter() } else { return serverRunNormal() diff --git a/frontend/app/store/wshclientapi.ts b/frontend/app/store/wshclientapi.ts index c1e3e23ac5..aeb93b3e1f 100644 --- a/frontend/app/store/wshclientapi.ts +++ b/frontend/app/store/wshclientapi.ts @@ -57,6 +57,11 @@ class RpcApiType { return client.wshRpcCall("connstatus", null, opts); } + // command "connupdatewsh" [call] + ConnUpdateWshCommand(client: WshClient, data: UpdateInfo, opts?: RpcOpts): Promise { + return client.wshRpcCall("connupdatewsh", data, opts); + } + // command "controllerinput" [call] ControllerInputCommand(client: WshClient, data: CommandBlockInputData, opts?: RpcOpts): Promise { return client.wshRpcCall("controllerinput", data, opts); diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index 056fe8ea9a..9f136502d3 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -729,6 +729,14 @@ declare global { activetabid: string; }; + // wshrpc.UpdateInfo + type UpdateInfo = { + host: string; + clientarch: string; + clientos: string; + string: string; + }; + // userinput.UserInputRequest type UserInputRequest = { requestid: string; diff --git a/pkg/remote/conncontroller/conncontroller.go b/pkg/remote/conncontroller/conncontroller.go index 5150681d41..35c71ecd10 100644 --- a/pkg/remote/conncontroller/conncontroller.go +++ b/pkg/remote/conncontroller/conncontroller.go @@ -307,6 +307,28 @@ func (wise *WshInstallSkipError) Error() string { return "skipping wsh installation" } +func (conn *SSHConn) UpdateWsh(ctx context.Context, clientDisplayName string, opts *WshInstallOpts) error { + if opts == nil { + opts = &WshInstallOpts{} + } + client := conn.GetClient() + if client == nil { + return fmt.Errorf("client is nil") + } + clientOs, clientArch, err := remote.GetClientPlatform(ctx, genconn.MakeSSHShellClient(client)) + if err != nil { + return err + } + // should be able to GetClientPlatform in other ways, but this works for now + err = remote.CpWshToRemote(ctx, client, clientOs, clientArch) + if err != nil { + return fmt.Errorf("error installing wsh to remote: %w", err) + } + log.Printf("successfully installed wsh on %s\n", conn.GetName()) + return nil + +} + func (conn *SSHConn) CheckAndInstallWsh(ctx context.Context, clientDisplayName string, opts *WshInstallOpts) error { if opts == nil { opts = &WshInstallOpts{} diff --git a/pkg/wshrpc/wshclient/wshclient.go b/pkg/wshrpc/wshclient/wshclient.go index e365ca68f0..912d954ef5 100644 --- a/pkg/wshrpc/wshclient/wshclient.go +++ b/pkg/wshrpc/wshclient/wshclient.go @@ -73,6 +73,12 @@ func ConnStatusCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]wshrpc.ConnSt return resp, err } +// command "connupdatewsh", wshserver.ConnUpdateWshCommand +func ConnUpdateWshCommand(w *wshutil.WshRpc, data wshrpc.UpdateInfo, opts *wshrpc.RpcOpts) (bool, error) { + resp, err := sendRpcRequestCallHelper[bool](w, "connupdatewsh", data, opts) + return resp, err +} + // command "controllerinput", wshserver.ControllerInputCommand func ControllerInputCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockInputData, opts *wshrpc.RpcOpts) error { _, err := sendRpcRequestCallHelper[any](w, "controllerinput", data, opts) diff --git a/pkg/wshrpc/wshrpctypes.go b/pkg/wshrpc/wshrpctypes.go index d28adf5c85..9d8564333d 100644 --- a/pkg/wshrpc/wshrpctypes.go +++ b/pkg/wshrpc/wshrpctypes.go @@ -84,6 +84,7 @@ const ( Command_WslList = "wsllist" Command_WslDefaultDistro = "wsldefaultdistro" Command_DismissWshFail = "dismisswshfail" + Command_ConnUpdateWsh = "updatewsh" Command_WorkspaceList = "workspacelist" @@ -162,6 +163,7 @@ type WshRpcInterface interface { WslListCommand(ctx context.Context) ([]string, error) WslDefaultDistroCommand(ctx context.Context) (string, error) DismissWshFailCommand(ctx context.Context, connName string) error + ConnUpdateWshCommand(ctx context.Context, updateInfo UpdateInfo) (bool, error) // eventrecv is special, it's handled internally by WshRpc with EventListener EventRecvCommand(ctx context.Context, data wps.WaveEvent) error @@ -492,6 +494,13 @@ type ConnRequest struct { Keywords ConnKeywords `json:"keywords,omitempty"` } +type UpdateInfo struct { + ConnName string `json:"host"` + ClientArch string `json:"clientarch"` + ClientOs string `json:"clientos"` + ClientVersion string `json:"string"` +} + const ( TimeSeries_Cpu = "cpu" ) diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index 0154c12333..b27f26b131 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -36,6 +36,7 @@ import ( "github.com/wavetermdev/waveterm/pkg/wshutil" "github.com/wavetermdev/waveterm/pkg/wsl" "github.com/wavetermdev/waveterm/pkg/wstore" + "golang.org/x/mod/semver" ) var InvalidWslDistroNames = []string{"docker-desktop", "docker-desktop-data"} @@ -665,6 +666,36 @@ func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, connName strin return conn.CheckAndInstallWsh(ctx, connName, &conncontroller.WshInstallOpts{Force: true, NoUserPrompt: true}) } +func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, updateInfo wshrpc.UpdateInfo) (bool, error) { + connName := updateInfo.ConnName + // check version number, return now if update not necessary + expectedVersion := fmt.Sprintf("v%s", wavebase.WaveVersion) + if semver.Compare(updateInfo.ClientVersion, expectedVersion) < 0 { + return false, nil + } + + // todo: need to add user input code here for validation + + if strings.HasPrefix(connName, "wsl://") { + return false, fmt.Errorf("cannot use this command for wsl connections") + } + connOpts, err := remote.ParseOpts(connName) + if err != nil { + return false, fmt.Errorf("error parsing connection name: %w", err) + } + conn := conncontroller.GetConn(ctx, connOpts, false, &wshrpc.ConnKeywords{}) + if conn == nil { + return false, fmt.Errorf("connection not found: %s", connName) + } + err = conn.UpdateWsh(ctx, connName, &conncontroller.WshInstallOpts{Force: true, NoUserPrompt: true}) + if err != nil { + return false, fmt.Errorf("update failed: %w", err) + } + + // todo: need to add code for modifying configs? + return true, nil +} + func (ws *WshServer) ConnListCommand(ctx context.Context) ([]string, error) { return conncontroller.GetConnectionsList() } From 79e5d793d44e9675807b4bf2921d2716ad1bfd4a Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 10 Jan 2025 15:52:56 -0800 Subject: [PATCH 2/7] fix: add generated files --- frontend/app/store/wshclientapi.ts | 63 ++++++++---------------------- pkg/wshrpc/wshclient/wshclient.go | 12 +++--- 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/frontend/app/store/wshclientapi.ts b/frontend/app/store/wshclientapi.ts index 33bb7dad02..aa9cddf9a6 100644 --- a/frontend/app/store/wshclientapi.ts +++ b/frontend/app/store/wshclientapi.ts @@ -57,20 +57,16 @@ class RpcApiType { return client.wshRpcCall("connstatus", null, opts); } - // command "controllerappendoutput" [call] - ControllerAppendOutputCommand( - client: WshClient, - data: CommandControllerAppendOutputData, - opts?: RpcOpts - ): Promise { - return client.wshRpcCall("controllerappendoutput", data, opts); - } - // command "connupdatewsh" [call] ConnUpdateWshCommand(client: WshClient, data: UpdateInfo, opts?: RpcOpts): Promise { return client.wshRpcCall("connupdatewsh", data, opts); } + // command "controllerappendoutput" [call] + ControllerAppendOutputCommand(client: WshClient, data: CommandControllerAppendOutputData, opts?: RpcOpts): Promise { + return client.wshRpcCall("controllerappendoutput", data, opts); + } + // command "controllerinput" [call] ControllerInputCommand(client: WshClient, data: CommandBlockInputData, opts?: RpcOpts): Promise { return client.wshRpcCall("controllerinput", data, opts); @@ -122,11 +118,7 @@ class RpcApiType { } // command "eventreadhistory" [call] - EventReadHistoryCommand( - client: WshClient, - data: CommandEventReadHistoryData, - opts?: RpcOpts - ): Promise { + EventReadHistoryCommand(client: WshClient, data: CommandEventReadHistoryData, opts?: RpcOpts): Promise { return client.wshRpcCall("eventreadhistory", data, opts); } @@ -256,16 +248,12 @@ class RpcApiType { } // command "remotestreamcpudata" [responsestream] - RemoteStreamCpuDataCommand(client: WshClient, opts?: RpcOpts): AsyncGenerator { + RemoteStreamCpuDataCommand(client: WshClient, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("remotestreamcpudata", null, opts); } // command "remotestreamfile" [responsestream] - RemoteStreamFileCommand( - client: WshClient, - data: CommandRemoteStreamFileData, - opts?: RpcOpts - ): AsyncGenerator { + RemoteStreamFileCommand(client: WshClient, data: CommandRemoteStreamFileData, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("remotestreamfile", data, opts); } @@ -275,11 +263,7 @@ class RpcApiType { } // command "resolveids" [call] - ResolveIdsCommand( - client: WshClient, - data: CommandResolveIdsData, - opts?: RpcOpts - ): Promise { + ResolveIdsCommand(client: WshClient, data: CommandResolveIdsData, opts?: RpcOpts): Promise { return client.wshRpcCall("resolveids", data, opts); } @@ -319,25 +303,17 @@ class RpcApiType { } // command "streamcpudata" [responsestream] - StreamCpuDataCommand( - client: WshClient, - data: CpuDataRequest, - opts?: RpcOpts - ): AsyncGenerator { + StreamCpuDataCommand(client: WshClient, data: CpuDataRequest, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("streamcpudata", data, opts); } // command "streamtest" [responsestream] - StreamTestCommand(client: WshClient, opts?: RpcOpts): AsyncGenerator { + StreamTestCommand(client: WshClient, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("streamtest", null, opts); } // command "streamwaveai" [responsestream] - StreamWaveAiCommand( - client: WshClient, - data: WaveAIStreamRequest, - opts?: RpcOpts - ): AsyncGenerator { + StreamWaveAiCommand(client: WshClient, data: WaveAIStreamRequest, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("streamwaveai", data, opts); } @@ -357,20 +333,12 @@ class RpcApiType { } // command "vdomrender" [responsestream] - VDomRenderCommand( - client: WshClient, - data: VDomFrontendUpdate, - opts?: RpcOpts - ): AsyncGenerator { + VDomRenderCommand(client: WshClient, data: VDomFrontendUpdate, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("vdomrender", data, opts); } // command "vdomurlrequest" [responsestream] - VDomUrlRequestCommand( - client: WshClient, - data: VDomUrlRequestData, - opts?: RpcOpts - ): AsyncGenerator { + VDomUrlRequestCommand(client: WshClient, data: VDomUrlRequestData, opts?: RpcOpts): AsyncGenerator { return client.wshRpcStream("vdomurlrequest", data, opts); } @@ -395,7 +363,7 @@ class RpcApiType { } // command "wshactivity" [call] - WshActivityCommand(client: WshClient, data: { [key: string]: number }, opts?: RpcOpts): Promise { + WshActivityCommand(client: WshClient, data: {[key: string]: number}, opts?: RpcOpts): Promise { return client.wshRpcCall("wshactivity", data, opts); } @@ -413,6 +381,7 @@ class RpcApiType { WslStatusCommand(client: WshClient, opts?: RpcOpts): Promise { return client.wshRpcCall("wslstatus", null, opts); } + } export const RpcApi = new RpcApiType(); diff --git a/pkg/wshrpc/wshclient/wshclient.go b/pkg/wshrpc/wshclient/wshclient.go index 162fd9aec3..1ea2cec1e8 100644 --- a/pkg/wshrpc/wshclient/wshclient.go +++ b/pkg/wshrpc/wshclient/wshclient.go @@ -73,18 +73,18 @@ func ConnStatusCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]wshrpc.ConnSt return resp, err } -// command "controllerappendoutput", wshserver.ControllerAppendOutputCommand -func ControllerAppendOutputCommand(w *wshutil.WshRpc, data wshrpc.CommandControllerAppendOutputData, opts *wshrpc.RpcOpts) error { - _, err := sendRpcRequestCallHelper[any](w, "controllerappendoutput", data, opts) - return err -} - // command "connupdatewsh", wshserver.ConnUpdateWshCommand func ConnUpdateWshCommand(w *wshutil.WshRpc, data wshrpc.UpdateInfo, opts *wshrpc.RpcOpts) (bool, error) { resp, err := sendRpcRequestCallHelper[bool](w, "connupdatewsh", data, opts) return resp, err } +// command "controllerappendoutput", wshserver.ControllerAppendOutputCommand +func ControllerAppendOutputCommand(w *wshutil.WshRpc, data wshrpc.CommandControllerAppendOutputData, opts *wshrpc.RpcOpts) error { + _, err := sendRpcRequestCallHelper[any](w, "controllerappendoutput", data, opts) + return err +} + // command "controllerinput", wshserver.ControllerInputCommand func ControllerInputCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockInputData, opts *wshrpc.RpcOpts) error { _, err := sendRpcRequestCallHelper[any](w, "controllerinput", data, opts) From 5d780072ffd0f29331d93f07893e803a6eb6ca00 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 10 Jan 2025 16:21:52 -0800 Subject: [PATCH 3/7] fix: adapt code to new convenience funcs This reduces a bit of duplication, both now, and for a future change. --- cmd/wsh/cmd/wshcmd-connserver.go | 10 ++-------- pkg/remote/conncontroller/conncontroller.go | 4 ++-- pkg/wshrpc/wshclient/wshclient.go | 2 +- pkg/wshrpc/wshrpctypes.go | 5 +++-- pkg/wshrpc/wshserver/wshserver.go | 11 +++++++---- pkg/wshutil/wshutil.go | 12 ++++++++++++ 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/cmd/wsh/cmd/wshcmd-connserver.go b/cmd/wsh/cmd/wshcmd-connserver.go index cde779dbc4..8d1b7bea88 100644 --- a/cmd/wsh/cmd/wshcmd-connserver.go +++ b/cmd/wsh/cmd/wshcmd-connserver.go @@ -11,7 +11,6 @@ import ( "net" "os" "path/filepath" - "runtime" "sync/atomic" "syscall" "time" @@ -191,13 +190,8 @@ func serverRunRouter() error { } func checkForUpdate() error { - updateInfo := wshrpc.UpdateInfo{ - ConnName: RpcContext.Conn, - ClientArch: runtime.GOARCH, - ClientOs: runtime.GOOS, - ClientVersion: wavebase.WaveVersion, - } - needsRestartRaw, err := RpcClient.SendRpcRequest(wshrpc.Command_ConnUpdateWsh, updateInfo, &wshrpc.RpcOpts{Timeout: 2000}) + remoteInfo := wshutil.GetInfo(RpcContext) + needsRestartRaw, err := RpcClient.SendRpcRequest(wshrpc.Command_ConnUpdateWsh, remoteInfo, &wshrpc.RpcOpts{Timeout: 60000}) if err != nil { return fmt.Errorf("could not update: %w", err) } diff --git a/pkg/remote/conncontroller/conncontroller.go b/pkg/remote/conncontroller/conncontroller.go index 664d5faaf5..30d7a2ffa2 100644 --- a/pkg/remote/conncontroller/conncontroller.go +++ b/pkg/remote/conncontroller/conncontroller.go @@ -228,7 +228,7 @@ func (conn *SSHConn) OpenDomainSocketListener(ctx context.Context) error { // expects the output of `wsh version` which looks like `wsh v0.10.4` or "not-installed" // returns (up-to-date, semver, error) // if not up to date, or error, version might be "" -func isWshVersionUpToDate(wshVersionLine string) (bool, string, error) { +func IsWshVersionUpToDate(wshVersionLine string) (bool, string, error) { wshVersionLine = strings.TrimSpace(wshVersionLine) if wshVersionLine == "not-installed" { return false, "", nil @@ -290,7 +290,7 @@ func (conn *SSHConn) StartConnServer(ctx context.Context) (bool, string, error) return false, "", fmt.Errorf("error reading wsh version: %w", err) } conn.Infof(ctx, "got connserver version: %s\n", strings.TrimSpace(versionLine)) - isUpToDate, clientVersion, err := isWshVersionUpToDate(versionLine) + isUpToDate, clientVersion, err := IsWshVersionUpToDate(versionLine) if err != nil { sshSession.Close() return false, "", fmt.Errorf("error checking wsh version: %w", err) diff --git a/pkg/wshrpc/wshclient/wshclient.go b/pkg/wshrpc/wshclient/wshclient.go index 1ea2cec1e8..6e8e8ece0d 100644 --- a/pkg/wshrpc/wshclient/wshclient.go +++ b/pkg/wshrpc/wshclient/wshclient.go @@ -74,7 +74,7 @@ func ConnStatusCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]wshrpc.ConnSt } // command "connupdatewsh", wshserver.ConnUpdateWshCommand -func ConnUpdateWshCommand(w *wshutil.WshRpc, data wshrpc.UpdateInfo, opts *wshrpc.RpcOpts) (bool, error) { +func ConnUpdateWshCommand(w *wshutil.WshRpc, data wshrpc.RemoteInfo, opts *wshrpc.RpcOpts) (bool, error) { resp, err := sendRpcRequestCallHelper[bool](w, "connupdatewsh", data, opts) return resp, err } diff --git a/pkg/wshrpc/wshrpctypes.go b/pkg/wshrpc/wshrpctypes.go index a368ef46bc..4bc9ea36ce 100644 --- a/pkg/wshrpc/wshrpctypes.go +++ b/pkg/wshrpc/wshrpctypes.go @@ -164,7 +164,7 @@ type WshRpcInterface interface { WslListCommand(ctx context.Context) ([]string, error) WslDefaultDistroCommand(ctx context.Context) (string, error) DismissWshFailCommand(ctx context.Context, connName string) error - ConnUpdateWshCommand(ctx context.Context, updateInfo UpdateInfo) (bool, error) + ConnUpdateWshCommand(ctx context.Context, updateInfo RemoteInfo) (bool, error) // eventrecv is special, it's handled internally by WshRpc with EventListener EventRecvCommand(ctx context.Context, data wps.WaveEvent) error @@ -502,11 +502,12 @@ type ConnRequest struct { LogBlockId string `json:"logblockid,omitempty"` } -type UpdateInfo struct { +type RemoteInfo struct { ConnName string `json:"host"` ClientArch string `json:"clientarch"` ClientOs string `json:"clientos"` ClientVersion string `json:"string"` + Shell string `json:"shell"` } const ( diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index 7a58d15fb8..5feab1ab3e 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -37,7 +37,6 @@ import ( "github.com/wavetermdev/waveterm/pkg/wshutil" "github.com/wavetermdev/waveterm/pkg/wsl" "github.com/wavetermdev/waveterm/pkg/wstore" - "golang.org/x/mod/semver" ) var InvalidWslDistroNames = []string{"docker-desktop", "docker-desktop-data"} @@ -700,11 +699,15 @@ func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, data wshrpc.Co return conn.InstallWsh(ctx) } -func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, updateInfo wshrpc.UpdateInfo) (bool, error) { +func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, updateInfo wshrpc.RemoteInfo) (bool, error) { connName := updateInfo.ConnName // check version number, return now if update not necessary - expectedVersion := fmt.Sprintf("v%s", wavebase.WaveVersion) - if semver.Compare(updateInfo.ClientVersion, expectedVersion) < 0 { + upToDate, _, err := conncontroller.IsWshVersionUpToDate(updateInfo.ClientVersion) + if err != nil { + return false, fmt.Errorf("unable to compare wsh version: %w", err) + } + if upToDate { + // no need to update return false, nil } diff --git a/pkg/wshutil/wshutil.go b/pkg/wshutil/wshutil.go index 9d6663bb9d..88b259aff2 100644 --- a/pkg/wshutil/wshutil.go +++ b/pkg/wshutil/wshutil.go @@ -12,6 +12,7 @@ import ( "net" "os" "os/signal" + "runtime" "sync" "sync/atomic" "syscall" @@ -536,3 +537,14 @@ func ExtractUnverifiedSocketName(tokenStr string) (string, error) { sockName = wavebase.ExpandHomeDirSafe(sockName) return sockName, nil } + +func GetInfo(rpcContext wshrpc.RpcContext) wshrpc.RemoteInfo { + return wshrpc.RemoteInfo{ + ConnName: rpcContext.Conn, + ClientArch: runtime.GOARCH, + ClientOs: runtime.GOOS, + ClientVersion: wavebase.WaveVersion, + Shell: os.Getenv("SHELL"), + } + +} From c8fd717470820000ffec879a02a2477ca96a067a Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 10 Jan 2025 19:20:51 -0800 Subject: [PATCH 4/7] fix: update generated type names --- frontend/app/store/wshclientapi.ts | 2 +- frontend/types/gotypes.d.ts | 17 +++++++++-------- pkg/wshrpc/wshrpctypes.go | 2 +- pkg/wshrpc/wshserver/wshserver.go | 6 +++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/frontend/app/store/wshclientapi.ts b/frontend/app/store/wshclientapi.ts index aa9cddf9a6..8d93b2cc41 100644 --- a/frontend/app/store/wshclientapi.ts +++ b/frontend/app/store/wshclientapi.ts @@ -58,7 +58,7 @@ class RpcApiType { } // command "connupdatewsh" [call] - ConnUpdateWshCommand(client: WshClient, data: UpdateInfo, opts?: RpcOpts): Promise { + ConnUpdateWshCommand(client: WshClient, data: RemoteInfo, opts?: RpcOpts): Promise { return client.wshRpcCall("connupdatewsh", data, opts); } diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index 18f050e6d1..c2294113a3 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -553,6 +553,15 @@ declare global { y: number; }; + // wshrpc.RemoteInfo + type RemoteInfo = { + host: string; + clientarch: string; + clientos: string; + string: string; + shell: string; + }; + // wshutil.RpcMessage type RpcMessage = { command?: string; @@ -746,14 +755,6 @@ declare global { activetabid: string; }; - // wshrpc.UpdateInfo - type UpdateInfo = { - host: string; - clientarch: string; - clientos: string; - string: string; - }; - // userinput.UserInputRequest type UserInputRequest = { requestid: string; diff --git a/pkg/wshrpc/wshrpctypes.go b/pkg/wshrpc/wshrpctypes.go index 4bc9ea36ce..a15a2d950d 100644 --- a/pkg/wshrpc/wshrpctypes.go +++ b/pkg/wshrpc/wshrpctypes.go @@ -164,7 +164,7 @@ type WshRpcInterface interface { WslListCommand(ctx context.Context) ([]string, error) WslDefaultDistroCommand(ctx context.Context) (string, error) DismissWshFailCommand(ctx context.Context, connName string) error - ConnUpdateWshCommand(ctx context.Context, updateInfo RemoteInfo) (bool, error) + ConnUpdateWshCommand(ctx context.Context, remoteInfo RemoteInfo) (bool, error) // eventrecv is special, it's handled internally by WshRpc with EventListener EventRecvCommand(ctx context.Context, data wps.WaveEvent) error diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index 5feab1ab3e..d6b053cefe 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -699,10 +699,10 @@ func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, data wshrpc.Co return conn.InstallWsh(ctx) } -func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, updateInfo wshrpc.RemoteInfo) (bool, error) { - connName := updateInfo.ConnName +func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, remoteInfo wshrpc.RemoteInfo) (bool, error) { + connName := remoteInfo.ConnName // check version number, return now if update not necessary - upToDate, _, err := conncontroller.IsWshVersionUpToDate(updateInfo.ClientVersion) + upToDate, _, err := conncontroller.IsWshVersionUpToDate(remoteInfo.ClientVersion) if err != nil { return false, fmt.Errorf("unable to compare wsh version: %w", err) } From c7a79057e7a194fe00da7281be091cc5f2bdcd0a Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 10 Jan 2025 19:53:20 -0800 Subject: [PATCH 5/7] fix: remove unnecessary os and arch sessions --- pkg/remote/conncontroller/conncontroller.go | 13 +++---------- pkg/wshrpc/wshrpctypes.go | 2 +- pkg/wshrpc/wshserver/wshserver.go | 3 ++- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/pkg/remote/conncontroller/conncontroller.go b/pkg/remote/conncontroller/conncontroller.go index 30d7a2ffa2..684151b705 100644 --- a/pkg/remote/conncontroller/conncontroller.go +++ b/pkg/remote/conncontroller/conncontroller.go @@ -377,20 +377,13 @@ to ensure a seamless experience. Would you like to install them? `) -func (conn *SSHConn) UpdateWsh(ctx context.Context, clientDisplayName string, opts *WshInstallOpts) error { - if opts == nil { - opts = &WshInstallOpts{} - } +func (conn *SSHConn) UpdateWsh(ctx context.Context, clientDisplayName string, remoteInfo *wshrpc.RemoteInfo) error { + log.Printf("attempting to update wsh for connection %v", remoteInfo) client := conn.GetClient() if client == nil { return fmt.Errorf("client is nil") } - clientOs, clientArch, err := remote.GetClientPlatform(ctx, genconn.MakeSSHShellClient(client)) - if err != nil { - return err - } - // should be able to GetClientPlatform in other ways, but this works for now - err = remote.CpWshToRemote(ctx, client, clientOs, clientArch) + err := remote.CpWshToRemote(ctx, client, remoteInfo.ClientOs, remoteInfo.ClientArch) if err != nil { return fmt.Errorf("error installing wsh to remote: %w", err) } diff --git a/pkg/wshrpc/wshrpctypes.go b/pkg/wshrpc/wshrpctypes.go index a15a2d950d..5c42ebd4d0 100644 --- a/pkg/wshrpc/wshrpctypes.go +++ b/pkg/wshrpc/wshrpctypes.go @@ -506,7 +506,7 @@ type RemoteInfo struct { ConnName string `json:"host"` ClientArch string `json:"clientarch"` ClientOs string `json:"clientos"` - ClientVersion string `json:"string"` + ClientVersion string `json:"clientversion"` Shell string `json:"shell"` } diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index d6b053cefe..5314bb5939 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -708,6 +708,7 @@ func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, remoteInfo wshrpc } if upToDate { // no need to update + log.Printf("wsh update is not needed for connection %s", connName) return false, nil } @@ -724,7 +725,7 @@ func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, remoteInfo wshrpc if conn == nil { return false, fmt.Errorf("connection not found: %s", connName) } - err = conn.UpdateWsh(ctx, connName, &conncontroller.WshInstallOpts{Force: true, NoUserPrompt: true}) + err = conn.UpdateWsh(ctx, connName, &remoteInfo) if err != nil { return false, fmt.Errorf("update failed: %w", err) } From 622138463ca994cdafa817d52961c5bf27052f5a Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 10 Jan 2025 21:09:05 -0800 Subject: [PATCH 6/7] refactor: update logs and error messages --- pkg/remote/conncontroller/conncontroller.go | 7 ++++--- pkg/wshrpc/wshserver/wshserver.go | 12 ++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pkg/remote/conncontroller/conncontroller.go b/pkg/remote/conncontroller/conncontroller.go index 684151b705..c341f583df 100644 --- a/pkg/remote/conncontroller/conncontroller.go +++ b/pkg/remote/conncontroller/conncontroller.go @@ -378,16 +378,17 @@ Would you like to install them? `) func (conn *SSHConn) UpdateWsh(ctx context.Context, clientDisplayName string, remoteInfo *wshrpc.RemoteInfo) error { - log.Printf("attempting to update wsh for connection %v", remoteInfo) + conn.Infof(ctx, "attempting to update wsh for connection %s (os:%s arch:%s version:%s)\n", + remoteInfo.ConnName, remoteInfo.ClientOs, remoteInfo.ClientArch, remoteInfo.ClientVersion) client := conn.GetClient() if client == nil { - return fmt.Errorf("client is nil") + return fmt.Errorf("cannot update wsh: ssh client is not connected") } err := remote.CpWshToRemote(ctx, client, remoteInfo.ClientOs, remoteInfo.ClientArch) if err != nil { return fmt.Errorf("error installing wsh to remote: %w", err) } - log.Printf("successfully installed wsh on %s\n", conn.GetName()) + conn.Infof(ctx, "successfully updated wsh on %s\n", conn.GetName()) return nil } diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index 5314bb5939..4077913739 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -701,21 +701,25 @@ func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, data wshrpc.Co func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, remoteInfo wshrpc.RemoteInfo) (bool, error) { connName := remoteInfo.ConnName - // check version number, return now if update not necessary + if connName == "" { + return false, fmt.Errorf("invalid remote info: missing connection name") + } + + log.Printf("checking wsh version for connection %s (current: %s)", connName, remoteInfo.ClientVersion) upToDate, _, err := conncontroller.IsWshVersionUpToDate(remoteInfo.ClientVersion) if err != nil { return false, fmt.Errorf("unable to compare wsh version: %w", err) } if upToDate { // no need to update - log.Printf("wsh update is not needed for connection %s", connName) + log.Printf("wsh is already up to date for connection %s", connName) return false, nil } // todo: need to add user input code here for validation if strings.HasPrefix(connName, "wsl://") { - return false, fmt.Errorf("cannot use this command for wsl connections") + return false, fmt.Errorf("connupdatewshcommand is not supported for wsl connections") } connOpts, err := remote.ParseOpts(connName) if err != nil { @@ -727,7 +731,7 @@ func (ws *WshServer) ConnUpdateWshCommand(ctx context.Context, remoteInfo wshrpc } err = conn.UpdateWsh(ctx, connName, &remoteInfo) if err != nil { - return false, fmt.Errorf("update failed: %w", err) + return false, fmt.Errorf("wsh update failed for connection %s: %w", connName, err) } // todo: need to add code for modifying configs? From c9461ba612e70b85de75452b26ae8a2e426f5fa5 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 10 Jan 2025 21:09:56 -0800 Subject: [PATCH 7/7] fix: update generated files --- frontend/types/gotypes.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index c2294113a3..604eb464d1 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -558,7 +558,7 @@ declare global { host: string; clientarch: string; clientos: string; - string: string; + clientversion: string; shell: string; };