remote install

This commit is contained in:
sawka 2022-09-26 23:23:04 -07:00
parent d143ca2820
commit a60680f855
2 changed files with 128 additions and 35 deletions

View File

@ -94,6 +94,7 @@ func init() {
registerCmdFn("remote:disconnect", RemoteDisconnectCommand)
registerCmdFn("remote:connect", RemoteConnectCommand)
registerCmdFn("remote:install", RemoteInstallCommand)
registerCmdFn("remote:installcancel", RemoteInstallCancelCommand)
registerCmdFn("window:resize", WindowResizeCommand)
@ -109,7 +110,7 @@ func getValidCommands() []string {
if val.IsAlias {
continue
}
rtn = append(rtn, key)
rtn = append(rtn, "/"+key)
}
return rtn
}
@ -416,7 +417,33 @@ func UnSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
}
func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
return nil, nil
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote)
if err != nil {
return nil, err
}
mshell := ids.Remote.MShell
go mshell.RunInstall()
return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("show remote [%s] info", ids.Remote.DisplayName),
PtyRemoteId: ids.Remote.RemotePtr.RemoteId,
},
}, nil
}
func RemoteInstallCancelCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote)
if err != nil {
return nil, err
}
mshell := ids.Remote.MShell
go mshell.CancelInstall()
return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("show remote [%s] info", ids.Remote.DisplayName),
PtyRemoteId: ids.Remote.RemotePtr.RemoteId,
},
}, nil
}
func RemoteConnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
@ -424,14 +451,13 @@ func RemoteConnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
if err != nil {
return nil, err
}
if ids.Remote.RState.IsConnected() {
return sstore.InfoMsgUpdate("remote %q already connected (no action taken)", ids.Remote.DisplayName), nil
}
if ids.Remote.RState.Status == remote.StatusConnecting {
return sstore.InfoMsgUpdate("remote %q is already trying to connect (no action taken)", ids.Remote.DisplayName), nil
}
go ids.Remote.MShell.Launch()
return sstore.InfoMsgUpdate("remote %q reconnecting", ids.Remote.DisplayName), nil
return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("show remote [%s] info", ids.Remote.DisplayName),
PtyRemoteId: ids.Remote.RemotePtr.RemoteId,
},
}, nil
}
func RemoteDisconnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
@ -440,16 +466,13 @@ func RemoteDisconnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketTy
return nil, err
}
force := resolveBool(pk.Kwargs["force"], false)
status := ids.Remote.MShell.GetStatus()
if status != remote.StatusConnected && status != remote.StatusConnecting {
return sstore.InfoMsgUpdate("remote %q already disconnected (no action taken)", ids.Remote.DisplayName), nil
}
numCommands := ids.Remote.MShell.GetNumRunningCommands()
if numCommands > 0 && !force {
return nil, fmt.Errorf("remote not disconnected, %q has %d running commands. use 'force=1' to force disconnection", ids.Remote.DisplayName)
}
ids.Remote.MShell.Disconnect()
return sstore.InfoMsgUpdate("remote %q disconnected", ids.Remote.DisplayName), nil
go ids.Remote.MShell.Disconnect(force)
return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("show remote [%s] info", ids.Remote.DisplayName),
PtyRemoteId: ids.Remote.RemotePtr.RemoteId,
},
}, nil
}
func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {

View File

@ -54,7 +54,6 @@ func MakeServerCommandStr() string {
}
const (
StatusInit = "init"
StatusConnected = "connected"
StatusConnecting = "connecting"
StatusDisconnected = "disconnected"
@ -94,7 +93,7 @@ type MShellProc struct {
InstallCancelFn context.CancelFunc
InstallErr error
RunningCmds []base.CommandKey
RunningCmds map[base.CommandKey]bool
}
type RemoteRuntimeState struct {
@ -106,6 +105,9 @@ type RemoteRuntimeState struct {
RemoteVars map[string]string `json:"remotevars"`
Status string `json:"status"`
ErrorStr string `json:"errorstr,omitempty"`
InstallStatus string `json:"installstatus"`
InstallErrorStr string `json:"installerrorstr,omitempty"`
NeedsMShellUpgrade bool `json:"needsmshellupgrade,omitempty"`
DefaultState *sstore.RemoteState `json:"defaultstate"`
ConnectMode string `json:"connectmode"`
AutoInstall bool `json:"autoinstall"`
@ -380,10 +382,15 @@ func (msh *MShellProc) GetRemoteRuntimeState() RemoteRuntimeState {
Archived: msh.Remote.Archived,
RemoteIdx: msh.Remote.RemoteIdx,
UName: msh.UName,
InstallStatus: msh.InstallStatus,
NeedsMShellUpgrade: msh.NeedsMShellUpgrade,
}
if msh.Err != nil {
state.ErrorStr = msh.Err.Error()
}
if msh.InstallErr != nil {
state.InstallErrorStr = msh.InstallErr.Error()
}
local := (msh.Remote.SSHOpts == nil || msh.Remote.SSHOpts.Local)
vars := make(map[string]string)
vars["user"] = msh.Remote.RemoteUser
@ -473,8 +480,10 @@ func MakeMShell(r *sstore.RemoteType) *MShellProc {
rtn := &MShellProc{
Lock: &sync.Mutex{},
Remote: r,
Status: StatusInit,
Status: StatusDisconnected,
PtyBuffer: buf,
InstallStatus: StatusDisconnected,
RunningCmds: make(map[base.CommandKey]bool),
}
rtn.WriteToPtyBuffer("console for remote [%s]\n", r.GetName())
return rtn
@ -544,6 +553,7 @@ func (msh *MShellProc) setErrorStatus(err error) {
}
func (msh *MShellProc) setInstallErrorStatus(err error) {
msh.WriteToPtyBuffer("*error, %s\n", err.Error())
msh.Lock.Lock()
defer msh.Lock.Unlock()
msh.InstallStatus = StatusError
@ -569,7 +579,17 @@ func (msh *MShellProc) GetNumRunningCommands() int {
return len(msh.RunningCmds)
}
func (msh *MShellProc) Disconnect() {
func (msh *MShellProc) Disconnect(force bool) {
status := msh.GetStatus()
if status != StatusConnected && status != StatusConnecting {
msh.WriteToPtyBuffer("remote already disconnected (no action taken)\n")
return
}
numCommands := msh.GetNumRunningCommands()
if numCommands > 0 && !force {
msh.WriteToPtyBuffer("remote not disconnected, has %d running commands. use force=1 to force disconnection\n", numCommands)
return
}
msh.Lock.Lock()
defer msh.Lock.Unlock()
if msh.ServerProc != nil {
@ -581,6 +601,15 @@ func (msh *MShellProc) Disconnect() {
}
}
func (msh *MShellProc) CancelInstall() {
msh.Lock.Lock()
defer msh.Lock.Unlock()
if msh.InstallCancelFn != nil {
msh.InstallCancelFn()
msh.InstallCancelFn = nil
}
}
func (msh *MShellProc) GetRemoteName() string {
msh.Lock.Lock()
defer msh.Lock.Unlock()
@ -652,12 +681,17 @@ func (msh *MShellProc) RunPtyReadLoop(cmdPty *os.File) {
func (msh *MShellProc) RunInstall() {
remoteCopy := msh.GetRemoteCopy()
if remoteCopy.Archived {
msh.WriteToPtyBuffer("cannot install on archived remote\n")
msh.WriteToPtyBuffer("*error: cannot install on archived remote\n")
return
}
baseStatus := msh.GetStatus()
if baseStatus == StatusConnecting || baseStatus == StatusConnected {
msh.WriteToPtyBuffer("*error: cannot install on remote that is connected/connecting, disconnect to install\n")
return
}
curStatus := msh.GetInstallStatus()
if curStatus == StatusConnecting {
msh.WriteToPtyBuffer("cannot install on remote that is already trying to install, cancel current install to try again")
msh.WriteToPtyBuffer("*error: cannot install on remote that is already trying to install, cancel current install to try again\n")
return
}
msh.WriteToPtyBuffer("installing mshell %s to %s...\n", MShellVersion, remoteCopy.RemoteCanonicalName)
@ -668,7 +702,6 @@ func (msh *MShellProc) RunInstall() {
cmdPty, err := msh.addControllingTty(ecmd)
if err != nil {
statusErr := fmt.Errorf("cannot attach controlling tty to mshell install command: %w", err)
msh.WriteToPtyBuffer("*error, %s\n", statusErr.Error())
msh.setInstallErrorStatus(statusErr)
return
}
@ -676,11 +709,13 @@ func (msh *MShellProc) RunInstall() {
if len(ecmd.ExtraFiles) > 0 {
ecmd.ExtraFiles[len(ecmd.ExtraFiles)-1].Close()
}
cmdPty.Close()
}()
go msh.RunPtyReadLoop(cmdPty)
clientCtx, clientCancelFn := context.WithCancel(context.Background())
defer clientCancelFn()
msh.WithLock(func() {
msh.InstallErr = nil
msh.InstallStatus = StatusConnecting
msh.InstallCancelFn = clientCancelFn
go msh.NotifyRemoteUpdate()
@ -689,13 +724,26 @@ func (msh *MShellProc) RunInstall() {
msh.WriteToPtyBuffer("%s", msg)
}
err = shexec.RunInstallFromCmd(clientCtx, ecmd, true, "", msgFn)
if err == context.Canceled {
msh.WriteToPtyBuffer("*install canceled\n")
msh.WithLock(func() {
msh.InstallStatus = StatusDisconnected
go msh.NotifyRemoteUpdate()
})
return
}
if err != nil {
statusErr := fmt.Errorf("install failed: %w", err)
msh.WriteToPtyBuffer("*error, %s\n", statusErr.Error())
msh.setInstallErrorStatus(statusErr)
return
}
msh.WithLock(func() {
msh.InstallStatus = StatusDisconnected
msh.InstallCancelFn = nil
msh.NeedsMShellUpgrade = false
})
msh.WriteToPtyBuffer("successfully installed mshell %s\n", MShellVersion)
go msh.NotifyRemoteUpdate()
return
}
@ -706,10 +754,19 @@ func (msh *MShellProc) Launch() {
return
}
curStatus := msh.GetStatus()
if curStatus == StatusConnected {
msh.WriteToPtyBuffer("remote is already connected (no action taken)\n")
return
}
if curStatus == StatusConnecting {
msh.WriteToPtyBuffer("remote is already connecting, disconnect before trying to connect again\n")
return
}
istatus := msh.GetInstallStatus()
if istatus == StatusConnecting {
msh.WriteToPtyBuffer("remote is trying to install, cancel install before trying to connect again\n")
return
}
msh.WriteToPtyBuffer("connecting to %s...\n", remoteCopy.RemoteCanonicalName)
sshOpts := convertSSHOpts(remoteCopy.SSHOpts)
sshOpts.SSHErrorsToTty = true
@ -731,6 +788,7 @@ func (msh *MShellProc) Launch() {
makeClientCtx, makeClientCancelFn := context.WithCancel(context.Background())
defer makeClientCancelFn()
msh.WithLock(func() {
msh.Err = nil
msh.Status = StatusConnecting
msh.MakeClientCancelFn = makeClientCancelFn
go msh.NotifyRemoteUpdate()
@ -750,7 +808,12 @@ func (msh *MShellProc) Launch() {
// no notify here, because we'll call notify in either case below
})
if err == context.Canceled {
err = fmt.Errorf("forced disconnection")
msh.WriteToPtyBuffer("*forced disconnection\n")
msh.WithLock(func() {
msh.Status = StatusDisconnected
go msh.NotifyRemoteUpdate()
})
return
}
if err == nil && semver.MajorMinor(mshellVersion) != semver.MajorMinor(MShellVersion) {
err = fmt.Errorf("mshell version is not compatible current=%s remote=%s", MShellVersion, mshellVersion)
@ -826,7 +889,7 @@ func (state RemoteRuntimeState) ExpandHomeDir(pathStr string) (string, error) {
func (msh *MShellProc) IsCmdRunning(ck base.CommandKey) bool {
msh.Lock.Lock()
defer msh.Lock.Unlock()
for _, runningCk := range msh.RunningCmds {
for runningCk, _ := range msh.RunningCmds {
if runningCk == ck {
return true
}
@ -921,7 +984,13 @@ func RunCommand(ctx context.Context, cmdId string, remotePtr sstore.RemotePtrTyp
func (msh *MShellProc) AddRunningCmd(ck base.CommandKey) {
msh.Lock.Lock()
defer msh.Lock.Unlock()
msh.RunningCmds = append(msh.RunningCmds, ck)
msh.RunningCmds[ck] = true
}
func (msh *MShellProc) RemoveRunningCmd(ck base.CommandKey) {
msh.Lock.Lock()
defer msh.Lock.Unlock()
delete(msh.RunningCmds, ck)
}
func (msh *MShellProc) PacketRpc(ctx context.Context, pk packet.RpcPacketType) (*packet.ResponsePacketType, error) {
@ -966,7 +1035,7 @@ func makeDataAckPacket(ck base.CommandKey, fdNum int, ackLen int, err error) *pa
}
func (msh *MShellProc) notifyHangups_nolock() {
for _, ck := range msh.RunningCmds {
for ck, _ := range msh.RunningCmds {
cmd, err := sstore.GetCmdById(context.Background(), ck.GetSessionId(), ck.GetCmdId())
if err != nil {
continue
@ -974,10 +1043,11 @@ func (msh *MShellProc) notifyHangups_nolock() {
update := sstore.ModelUpdate{Cmd: cmd}
sstore.MainBus.SendUpdate(ck.GetSessionId(), update)
}
msh.RunningCmds = nil
msh.RunningCmds = make(map[base.CommandKey]bool)
}
func (msh *MShellProc) handleCmdDonePacket(donePk *packet.CmdDonePacketType) {
msh.RemoveRunningCmd(donePk.CK)
update, err := sstore.UpdateCmdDonePk(context.Background(), donePk)
if err != nil {
msh.WriteToPtyBuffer("[error] updating cmddone: %v\n", err)