mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
implement wsh conn commands (#319)
This commit is contained in:
parent
9b720ef455
commit
7bc154771a
111
cmd/wsh/cmd/wshcmd-conn.go
Normal file
111
cmd/wsh/cmd/wshcmd-conn.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/remote"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
var connCmd = &cobra.Command{
|
||||||
|
Use: "conn [status|reinstall|disconnect|connect|ensure] [connection-name]",
|
||||||
|
Short: "implements connection commands",
|
||||||
|
Args: cobra.RangeArgs(1, 2),
|
||||||
|
RunE: connRun,
|
||||||
|
PreRunE: preRunSetupRpcClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(connCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func connStatus() error {
|
||||||
|
resp, err := wshclient.ConnStatusCommand(RpcClient, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getting connection status: %w", err)
|
||||||
|
}
|
||||||
|
if len(resp) == 0 {
|
||||||
|
WriteStdout("no connections\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
WriteStdout("%-30s %-12s\n", "connection", "status")
|
||||||
|
WriteStdout("----------------------------------------------\n")
|
||||||
|
for _, conn := range resp {
|
||||||
|
str := fmt.Sprintf("%-30s %-12s", conn.Connection, conn.Status)
|
||||||
|
if conn.Error != "" {
|
||||||
|
str += fmt.Sprintf(" (%s)", conn.Error)
|
||||||
|
}
|
||||||
|
str += "\n"
|
||||||
|
WriteStdout("%s\n", str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connEnsure(connName string) error {
|
||||||
|
err := wshclient.ConnEnsureCommand(RpcClient, connName, &wshrpc.RpcOpts{Timeout: 60000})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ensuring connection: %w", err)
|
||||||
|
}
|
||||||
|
WriteStdout("wsh ensured on connection %q\n", connName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connReinstall(connName string) error {
|
||||||
|
err := wshclient.ConnReinstallWshCommand(RpcClient, connName, &wshrpc.RpcOpts{Timeout: 60000})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("reinstalling connection: %w", err)
|
||||||
|
}
|
||||||
|
WriteStdout("wsh reinstalled on connection %q\n", connName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connDisconnect(connName string) error {
|
||||||
|
err := wshclient.ConnDisconnectCommand(RpcClient, connName, &wshrpc.RpcOpts{Timeout: 10000})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("disconnecting connection: %w", err)
|
||||||
|
}
|
||||||
|
WriteStdout("disconnected connection %q\n", connName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connConnect(connName string) error {
|
||||||
|
err := wshclient.ConnConnectCommand(RpcClient, connName, &wshrpc.RpcOpts{Timeout: 60000})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connecting connection: %w", err)
|
||||||
|
}
|
||||||
|
WriteStdout("connected connection %q\n", connName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connRun(cmd *cobra.Command, args []string) error {
|
||||||
|
connCmd := args[0]
|
||||||
|
var connName string
|
||||||
|
if connCmd != "status" {
|
||||||
|
if len(args) < 2 {
|
||||||
|
return fmt.Errorf("connection name is required")
|
||||||
|
}
|
||||||
|
connName = args[1]
|
||||||
|
_, err := remote.ParseOpts(connName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot parse connection name: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if connCmd == "status" {
|
||||||
|
return connStatus()
|
||||||
|
} else if connCmd == "ensure" {
|
||||||
|
return connEnsure(connName)
|
||||||
|
} else if connCmd == "reinstall" {
|
||||||
|
return connReinstall(connName)
|
||||||
|
} else if connCmd == "disconnect" {
|
||||||
|
return connDisconnect(connName)
|
||||||
|
} else if connCmd == "connect" {
|
||||||
|
return connConnect(connName)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unknown command %q", connCmd)
|
||||||
|
}
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/remote"
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshclient"
|
|
||||||
)
|
|
||||||
|
|
||||||
var connReinstallCmd = &cobra.Command{
|
|
||||||
Use: "connreinstall",
|
|
||||||
Short: "reinstall wsh on a connection",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: connReinstallRun,
|
|
||||||
PreRunE: preRunSetupRpcClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(connReinstallCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func connReinstallRun(cmd *cobra.Command, args []string) {
|
|
||||||
connName := args[0]
|
|
||||||
_, err := remote.ParseOpts(connName)
|
|
||||||
if err != nil {
|
|
||||||
WriteStderr("[error] cannot parse connection name: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = wshclient.ConnReinstallWshCommand(RpcClient, connName, &wshrpc.RpcOpts{Timeout: 60000})
|
|
||||||
if err != nil {
|
|
||||||
WriteStderr("[error] getting metadata: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
WriteStdout("wsh reinstalled on connection %q\n", connName)
|
|
||||||
}
|
|
@ -589,9 +589,9 @@ function subscribeToConnEvents() {
|
|||||||
try {
|
try {
|
||||||
const connStatus = event.data as ConnStatus;
|
const connStatus = event.data as ConnStatus;
|
||||||
if (connStatus == null || util.isBlank(connStatus.connection)) {
|
if (connStatus == null || util.isBlank(connStatus.connection)) {
|
||||||
console.log("connchange2 early return");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log("connstatus update", connStatus);
|
||||||
let curAtom = getConnStatusAtom(connStatus.connection);
|
let curAtom = getConnStatusAtom(connStatus.connection);
|
||||||
globalStore.set(curAtom, connStatus);
|
globalStore.set(curAtom, connStatus);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -603,7 +603,13 @@ function subscribeToConnEvents() {
|
|||||||
function getConnStatusAtom(conn: string): jotai.PrimitiveAtom<ConnStatus> {
|
function getConnStatusAtom(conn: string): jotai.PrimitiveAtom<ConnStatus> {
|
||||||
let rtn = ConnStatusMap.get(conn);
|
let rtn = ConnStatusMap.get(conn);
|
||||||
if (rtn == null) {
|
if (rtn == null) {
|
||||||
const connStatus: ConnStatus = { connection: conn, connected: false, error: null, status: "disconnected" };
|
const connStatus: ConnStatus = {
|
||||||
|
connection: conn,
|
||||||
|
connected: false,
|
||||||
|
error: null,
|
||||||
|
status: "disconnected",
|
||||||
|
hasconnected: false,
|
||||||
|
};
|
||||||
rtn = jotai.atom(connStatus);
|
rtn = jotai.atom(connStatus);
|
||||||
ConnStatusMap.set(conn, rtn);
|
ConnStatusMap.set(conn, rtn);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,11 @@ class WshServerType {
|
|||||||
return WOS.wshServerRpcHelper_call("authenticate", data, opts);
|
return WOS.wshServerRpcHelper_call("authenticate", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command "connconnect" [call]
|
||||||
|
ConnConnectCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||||
|
return WOS.wshServerRpcHelper_call("connconnect", data, opts);
|
||||||
|
}
|
||||||
|
|
||||||
// command "conndisconnect" [call]
|
// command "conndisconnect" [call]
|
||||||
ConnDisconnectCommand(data: string, opts?: RpcOpts): Promise<void> {
|
ConnDisconnectCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("conndisconnect", data, opts);
|
return WOS.wshServerRpcHelper_call("conndisconnect", data, opts);
|
||||||
@ -27,16 +32,16 @@ class WshServerType {
|
|||||||
return WOS.wshServerRpcHelper_call("connensure", data, opts);
|
return WOS.wshServerRpcHelper_call("connensure", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "connforceconnect" [call]
|
|
||||||
ConnForceConnectCommand(data: string, opts?: RpcOpts): Promise<void> {
|
|
||||||
return WOS.wshServerRpcHelper_call("connforceconnect", data, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
// command "connreinstallwsh" [call]
|
// command "connreinstallwsh" [call]
|
||||||
ConnReinstallWshCommand(data: string, opts?: RpcOpts): Promise<void> {
|
ConnReinstallWshCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("connreinstallwsh", data, opts);
|
return WOS.wshServerRpcHelper_call("connreinstallwsh", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command "connstatus" [call]
|
||||||
|
ConnStatusCommand(opts?: RpcOpts): Promise<ConnStatus[]> {
|
||||||
|
return WOS.wshServerRpcHelper_call("connstatus", null, opts);
|
||||||
|
}
|
||||||
|
|
||||||
// command "controllerinput" [call]
|
// command "controllerinput" [call]
|
||||||
ControllerInputCommand(data: CommandBlockInputData, opts?: RpcOpts): Promise<void> {
|
ControllerInputCommand(data: CommandBlockInputData, opts?: RpcOpts): Promise<void> {
|
||||||
return WOS.wshServerRpcHelper_call("controllerinput", data, opts);
|
return WOS.wshServerRpcHelper_call("controllerinput", data, opts);
|
||||||
|
@ -328,6 +328,30 @@ func (conn *SSHConn) Reconnect(ctx context.Context) error {
|
|||||||
return conn.Connect(ctx)
|
return conn.Connect(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *SSHConn) WaitForConnect(ctx context.Context) error {
|
||||||
|
for {
|
||||||
|
status := conn.DeriveConnStatus()
|
||||||
|
if status.Status == Status_Connected {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if status.Status == Status_Connecting {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return fmt.Errorf("context timeout")
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if status.Status == Status_Init || status.Status == Status_Disconnected {
|
||||||
|
return fmt.Errorf("disconnected")
|
||||||
|
}
|
||||||
|
if status.Status == Status_Error {
|
||||||
|
return fmt.Errorf("error: %v", status.Error)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unknown status: %q", status.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// does not return an error since that error is stored inside of SSHConn
|
// does not return an error since that error is stored inside of SSHConn
|
||||||
func (conn *SSHConn) Connect(ctx context.Context) error {
|
func (conn *SSHConn) Connect(ctx context.Context) error {
|
||||||
var connectAllowed bool
|
var connectAllowed bool
|
||||||
|
@ -24,6 +24,12 @@ func AuthenticateCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) (
|
|||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command "connconnect", wshserver.ConnConnectCommand
|
||||||
|
func ConnConnectCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||||
|
_, err := sendRpcRequestCallHelper[any](w, "connconnect", data, opts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// command "conndisconnect", wshserver.ConnDisconnectCommand
|
// command "conndisconnect", wshserver.ConnDisconnectCommand
|
||||||
func ConnDisconnectCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
func ConnDisconnectCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "conndisconnect", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "conndisconnect", data, opts)
|
||||||
@ -36,18 +42,18 @@ func ConnEnsureCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// command "connforceconnect", wshserver.ConnForceConnectCommand
|
|
||||||
func ConnForceConnectCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "connforceconnect", data, opts)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// command "connreinstallwsh", wshserver.ConnReinstallWshCommand
|
// command "connreinstallwsh", wshserver.ConnReinstallWshCommand
|
||||||
func ConnReinstallWshCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
func ConnReinstallWshCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "connreinstallwsh", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "connreinstallwsh", data, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command "connstatus", wshserver.ConnStatusCommand
|
||||||
|
func ConnStatusCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]wshrpc.ConnStatus, error) {
|
||||||
|
resp, err := sendRpcRequestCallHelper[[]wshrpc.ConnStatus](w, "connstatus", nil, opts)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// command "controllerinput", wshserver.ControllerInputCommand
|
// command "controllerinput", wshserver.ControllerInputCommand
|
||||||
func ControllerInputCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockInputData, opts *wshrpc.RpcOpts) error {
|
func ControllerInputCommand(w *wshutil.WshRpc, data wshrpc.CommandBlockInputData, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "controllerinput", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "controllerinput", data, opts)
|
||||||
|
@ -66,7 +66,7 @@ const (
|
|||||||
|
|
||||||
Command_ConnEnsure = "connensure"
|
Command_ConnEnsure = "connensure"
|
||||||
Command_ConnReinstallWsh = "connreinstallwsh"
|
Command_ConnReinstallWsh = "connreinstallwsh"
|
||||||
Command_ConnForceConnect = "connforceconnect"
|
Command_ConnConnect = "connconnect"
|
||||||
Command_ConnDisconnect = "conndisconnect"
|
Command_ConnDisconnect = "conndisconnect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -104,9 +104,10 @@ type WshRpcInterface interface {
|
|||||||
SetConfigCommand(ctx context.Context, data wconfig.MetaSettingsType) error
|
SetConfigCommand(ctx context.Context, data wconfig.MetaSettingsType) error
|
||||||
|
|
||||||
// connection functions
|
// connection functions
|
||||||
|
ConnStatusCommand(ctx context.Context) ([]ConnStatus, error)
|
||||||
ConnEnsureCommand(ctx context.Context, connName string) error
|
ConnEnsureCommand(ctx context.Context, connName string) error
|
||||||
ConnReinstallWshCommand(ctx context.Context, connName string) error
|
ConnReinstallWshCommand(ctx context.Context, connName string) error
|
||||||
ConnForceConnectCommand(ctx context.Context, connName string) error
|
ConnConnectCommand(ctx context.Context, connName string) error
|
||||||
ConnDisconnectCommand(ctx context.Context, connName string) error
|
ConnDisconnectCommand(ctx context.Context, connName string) error
|
||||||
|
|
||||||
// eventrecv is special, it's handled internally by WshRpc with EventListener
|
// eventrecv is special, it's handled internally by WshRpc with EventListener
|
||||||
|
@ -470,16 +470,57 @@ func (ws *WshServer) SetConfigCommand(ctx context.Context, data wconfig.MetaSett
|
|||||||
return wconfig.SetBaseConfigValue(data.MetaMapType)
|
return wconfig.SetBaseConfigValue(data.MetaMapType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ws *WshServer) ConnStatusCommand(ctx context.Context) ([]wshrpc.ConnStatus, error) {
|
||||||
|
rtn := conncontroller.GetAllConnStatus()
|
||||||
|
return rtn, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ws *WshServer) ConnEnsureCommand(ctx context.Context, connName string) error {
|
func (ws *WshServer) ConnEnsureCommand(ctx context.Context, connName string) error {
|
||||||
return nil
|
connOpts, err := remote.ParseOpts(connName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing connection name: %w", err)
|
||||||
|
}
|
||||||
|
conn := conncontroller.GetConn(ctx, connOpts, false)
|
||||||
|
if conn == nil {
|
||||||
|
return fmt.Errorf("connection not found: %s", connName)
|
||||||
|
}
|
||||||
|
connStatus := conn.DeriveConnStatus()
|
||||||
|
switch connStatus.Status {
|
||||||
|
case conncontroller.Status_Connected:
|
||||||
|
return nil
|
||||||
|
case conncontroller.Status_Connecting:
|
||||||
|
return conn.WaitForConnect(ctx)
|
||||||
|
case conncontroller.Status_Init, conncontroller.Status_Disconnected:
|
||||||
|
return conn.Connect(ctx)
|
||||||
|
case conncontroller.Status_Error:
|
||||||
|
return fmt.Errorf("connection error: %s", connStatus.Error)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown connection status %q", connStatus.Status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) ConnDisconnectCommand(ctx context.Context, connName string) error {
|
func (ws *WshServer) ConnDisconnectCommand(ctx context.Context, connName string) error {
|
||||||
return nil
|
connOpts, err := remote.ParseOpts(connName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing connection name: %w", err)
|
||||||
|
}
|
||||||
|
conn := conncontroller.GetConn(ctx, connOpts, false)
|
||||||
|
if conn == nil {
|
||||||
|
return fmt.Errorf("connection not found: %s", connName)
|
||||||
|
}
|
||||||
|
return conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) ConnForceConnectCommand(ctx context.Context, connName string) error {
|
func (ws *WshServer) ConnConnectCommand(ctx context.Context, connName string) error {
|
||||||
return nil
|
connOpts, err := remote.ParseOpts(connName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing connection name: %w", err)
|
||||||
|
}
|
||||||
|
conn := conncontroller.GetConn(ctx, connOpts, false)
|
||||||
|
if conn == nil {
|
||||||
|
return fmt.Errorf("connection not found: %s", connName)
|
||||||
|
}
|
||||||
|
return conn.Connect(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, connName string) error {
|
func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, connName string) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user