implement wsh conn commands (#319)

This commit is contained in:
Mike Sawka 2024-09-03 23:04:19 -07:00 committed by GitHub
parent 9b720ef455
commit 7bc154771a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 213 additions and 57 deletions

111
cmd/wsh/cmd/wshcmd-conn.go Normal file
View 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)
}
}

View File

@ -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)
}

View File

@ -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);
} }

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 {