update statediff algorithm for wavesrv / remote instances (#530)

* remote statemap from waveshell server (diff against initial state)

* move ShellStatePtr from sstore to packet so it can be passed over the wire

* add finalstatebaseptr to cmddone

* much improved diff computation code on wavesrv side

* fix displayname -- now using hash

* add comments, change a couple msh.WriteToPtyBuffer calls to log.Printfs
This commit is contained in:
Mike Sawka 2024-03-28 16:56:39 -07:00 committed by GitHub
parent 5c85b2b786
commit a1e4e807cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 182 additions and 108 deletions

View File

@ -743,13 +743,14 @@ func MakeCmdFinalPacket(ck base.CommandKey) *CmdFinalPacketType {
}
type CmdDonePacketType struct {
Type string `json:"type"`
Ts int64 `json:"ts"`
CK base.CommandKey `json:"ck"`
ExitCode int `json:"exitcode"`
DurationMs int64 `json:"durationms"`
FinalState *ShellState `json:"finalstate,omitempty"`
FinalStateDiff *ShellStateDiff `json:"finalstatediff,omitempty"`
Type string `json:"type"`
Ts int64 `json:"ts"`
CK base.CommandKey `json:"ck"`
ExitCode int `json:"exitcode"`
DurationMs int64 `json:"durationms"`
FinalState *ShellState `json:"finalstate,omitempty"`
FinalStateDiff *ShellStateDiff `json:"finalstatediff,omitempty"`
FinalStateBasePtr *ShellStatePtr `json:"finalstatebaseptr,omitempty"`
}
func (*CmdDonePacketType) GetType() string {
@ -814,10 +815,10 @@ type RunPacketType struct {
Type string `json:"type"`
ReqId string `json:"reqid"`
CK base.CommandKey `json:"ck"`
ShellType string `json:"shelltype"` // new in v0.6.0 (either "bash" or "zsh") (set by remote.go)
ShellType string `json:"shelltype"` // added in Wave v0.6.0 (either "bash" or "zsh") (set by remote.go)
Command string `json:"command"`
State *ShellState `json:"state,omitempty"`
StateDiff *ShellStateDiff `json:"statediff,omitempty"`
StatePtr *ShellStatePtr `json:"stateptr,omitempty"` // added in Wave v0.7.2
StateComplete bool `json:"statecomplete,omitempty"` // set to true if state is complete (the default env should not be set)
UsePty bool `json:"usepty,omitempty"`
TermOpts *TermOpts `json:"termopts,omitempty"`

View File

@ -40,6 +40,18 @@ type ShellState struct {
HashVal string `json:"-"`
}
type ShellStatePtr struct {
BaseHash string `json:"basehash"`
DiffHashArr []string `json:"diffhasharr,omitempty"`
}
func (ssptr *ShellStatePtr) IsEmpty() bool {
if ssptr == nil || ssptr.BaseHash == "" {
return true
}
return false
}
func (state ShellState) ApproximateSize() int64 {
return int64(len(state.Version) + len(state.Cwd) + len(state.ShellVars) + len(state.Aliases) + len(state.Funcs) + len(state.Error))
}

View File

@ -50,7 +50,6 @@ type MServer struct {
Sender *packet.PacketSender
ClientMap map[base.CommandKey]*shexec.ClientProc
Debug bool
StateMap *ShellStateMap
WriteErrorCh chan bool // closed if there is a I/O write error
WriteErrorChOnce *sync.Once
Done bool
@ -338,11 +337,6 @@ func (m *MServer) reinit(reqId string, shellType string) {
m.Sender.SendErrorResponse(reqId, fmt.Errorf("error initializing shell: %w", err))
return
}
err = m.StateMap.SetCurrentState(ssPk.State.GetShellType(), ssPk.State)
if err != nil {
m.Sender.SendErrorResponse(reqId, fmt.Errorf("error setting current state: %w", err))
return
}
ssPk.RespId = reqId
m.Sender.SendPacket(ssPk)
}
@ -710,7 +704,7 @@ func (m *MServer) ProcessRpcPacket(pk packet.RpcPacketType) {
m.Sender.SendErrorResponse(reqId, fmt.Errorf("invalid rpc type '%s'", pk.GetType()))
}
func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType) {
func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType, runPk *packet.RunPacketType) {
if pk.GetType() != packet.CmdDonePacketStr {
return
}
@ -718,25 +712,22 @@ func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType) {
if donePk.FinalState == nil {
return
}
stateHash, curState := m.StateMap.GetCurrentState(shellType)
if curState == nil {
initialState := runPk.State
if initialState == nil {
return
}
sapi, err := shellapi.MakeShellApi(curState.GetShellType())
initialStateHash := initialState.GetHashVal(false)
sapi, err := shellapi.MakeShellApi(initialState.GetShellType())
if err != nil {
return
}
diff, err := sapi.MakeShellStateDiff(curState, stateHash, donePk.FinalState)
diff, err := sapi.MakeShellStateDiff(initialState, initialStateHash, donePk.FinalState)
if err != nil {
return
}
donePk.FinalState = nil
donePk.FinalStateDiff = diff
}
func (m *MServer) isShellInitialized(shellType string) bool {
_, curState := m.StateMap.GetCurrentState(shellType)
return curState != nil
donePk.FinalStateBasePtr = runPk.StatePtr
}
func (m *MServer) runCommand(runPacket *packet.RunPacketType) {
@ -786,7 +777,7 @@ func (m *MServer) runCommand(runPacket *packet.RunPacketType) {
}()
shexec.SendRunPacketAndRunData(context.Background(), cproc.Input, runPacket)
cproc.ProxySingleOutput(runPacket.CK, m.Sender, func(pk packet.PacketType) {
m.clientPacketCallback(runPacket.ShellType, pk)
m.clientPacketCallback(runPacket.ShellType, pk, runPacket)
})
}()
}
@ -849,7 +840,6 @@ func RunServer() (int, error) {
server := &MServer{
Lock: &sync.Mutex{},
ClientMap: make(map[base.CommandKey]*shexec.ClientProc),
StateMap: MakeShellStateMap(),
Debug: debug,
WriteErrorCh: make(chan bool),
WriteErrorChOnce: &sync.Once{},

View File

@ -63,6 +63,9 @@ type ShellStateOutput struct {
Error string
}
// some timing info
// MakeShellStateDiff takes ~1ms to run (even on a large diff)
type ShellApi interface {
GetShellType() string
MakeExitTrap(fdNum int) (string, []byte)

View File

@ -186,6 +186,9 @@ func readEncodedStringArray(buf *bytes.Buffer) ([]string, error) {
func (rtn *LineDiffType) Decode(diffBytes []byte) error {
rtn.Clear()
if len(diffBytes) == 0 {
return nil
}
r := bytes.NewBuffer(diffBytes)
version, err := binary.ReadUvarint(r)
if err != nil {

View File

@ -29,7 +29,7 @@ func (diff *MapDiffType) Clear() {
}
func (diff MapDiffType) Dump() {
fmt.Printf("VAR-DIFF\n")
fmt.Printf("VAR-DIFF +%d -%d\n", len(diff.ToAdd), len(diff.ToRemove))
for name, val := range diff.ToAdd {
fmt.Printf(" add[%s] %s\n", name, val)
}
@ -111,6 +111,9 @@ func (diff MapDiffType) Encode() []byte {
func (diff *MapDiffType) Decode(diffBytes []byte) error {
diff.Clear()
if len(diffBytes) == 0 {
return nil
}
r := bytes.NewBuffer(diffBytes)
version, err := binpack.UnpackUInt(r)
if err != nil {

View File

@ -3862,7 +3862,7 @@ func doAsyncResetCommand(msh *remote.MShellProc, opts connectOptsType, cmd *ssto
rtnErr = err
return
}
newStatePtr := sstore.ShellStatePtr{
newStatePtr := packet.ShellStatePtr{
BaseHash: ssPk.State.GetHashVal(false),
}
if opts.Verbose && origStatePtr != nil {

View File

@ -12,6 +12,7 @@ import (
"strings"
"github.com/google/uuid"
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
"github.com/wavetermdev/waveterm/wavesrv/pkg/remote"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scpacket"
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
@ -42,7 +43,7 @@ type ResolvedRemote struct {
RState remote.RemoteRuntimeState
RemoteCopy *sstore.RemoteType
ShellType string // default remote shell preference
StatePtr *sstore.ShellStatePtr
StatePtr *packet.ShellStatePtr
FeState map[string]string
}
@ -491,7 +492,7 @@ func ResolveRemoteFromPtr(ctx context.Context, rptr *sstore.RemotePtrType, sessi
rtn.StatePtr = nil
rtn.FeState = nil
} else {
rtn.StatePtr = &sstore.ShellStatePtr{BaseHash: ri.StateBaseHash, DiffHashArr: ri.StateDiffHashArr}
rtn.StatePtr = &packet.ShellStatePtr{BaseHash: ri.StateBaseHash, DiffHashArr: ri.StateDiffHashArr}
rtn.FeState = ri.FeState
rtn.ShellType = ri.ShellType
}

View File

@ -1886,7 +1886,7 @@ type RunCommandOpts struct {
// optional, if not provided shellstate will look up state from remote instance
// ReturnState cannot be used with StatePtr
// this will also cause this command to bypass the pending state cmd logic
StatePtr *sstore.ShellStatePtr
StatePtr *packet.ShellStatePtr
// set to true to skip creating the pty file (for restarted commands)
NoCreateCmdPtyFile bool
@ -1920,6 +1920,9 @@ func RunCommand(ctx context.Context, rcOpts RunCommandOpts, runPacket *packet.Ru
if rcOpts.StatePtr != nil && runPacket.ReturnState {
return nil, nil, fmt.Errorf("RunCommand: cannot use ReturnState with StatePtr")
}
if runPacket.StatePtr != nil {
return nil, nil, fmt.Errorf("runPacket.StatePtr should not be set, it is set in RunCommand")
}
// pending state command logic
// if we are currently running a command that can change the state, we need to wait for it to finish
@ -1950,7 +1953,7 @@ func RunCommand(ctx context.Context, rcOpts RunCommandOpts, runPacket *packet.Ru
}
// get current remote-instance state
var statePtr *sstore.ShellStatePtr
var statePtr *packet.ShellStatePtr
if rcOpts.StatePtr != nil {
statePtr = rcOpts.StatePtr
} else {
@ -1963,6 +1966,8 @@ func RunCommand(ctx context.Context, rcOpts RunCommandOpts, runPacket *packet.Ru
return nil, nil, fmt.Errorf("cannot run command: no valid shell state found")
}
}
// statePtr will not be nil
runPacket.StatePtr = statePtr
currentState, err := sstore.GetFullState(ctx, *statePtr)
if err != nil || currentState == nil {
return nil, nil, fmt.Errorf("cannot load current remote state: %w", err)
@ -2216,31 +2221,95 @@ func (msh *MShellProc) notifyHangups_nolock() {
msh.PendingStateCmds = make(map[pendingStateKey]base.CommandKey)
}
// either fullstate or statediff will be set (not both) <- this is so the result is compatible with the sstore.UpdateRemoteState function
// note that this function *does* touch the DB, if FinalStateDiff is set, will ensure that StateBase is written to DB
func (msh *MShellProc) makeStatePtrFromFinalState(ctx context.Context, donePk *packet.CmdDonePacketType) (*sstore.ShellStatePtr, map[string]string, *packet.ShellState, *packet.ShellStateDiff, error) {
func (msh *MShellProc) resolveFinalState(ctx context.Context, origState *packet.ShellState, origStatePtr *packet.ShellStatePtr, donePk *packet.CmdDonePacketType) (*packet.ShellState, error) {
if donePk.FinalState != nil {
if origStatePtr == nil {
return nil, fmt.Errorf("command must have a stateptr to resolve final state")
}
finalState := stripScVarsFromState(donePk.FinalState)
feState := sstore.FeStateFromShellState(finalState)
statePtr := &sstore.ShellStatePtr{BaseHash: finalState.GetHashVal(false)}
return statePtr, feState, finalState, nil, nil
return finalState, nil
}
if donePk.FinalStateDiff != nil {
if donePk.FinalStateBasePtr == nil {
return nil, fmt.Errorf("invalid rtnstate, has diff but no baseptr")
}
stateDiff := stripScVarsFromStateDiff(donePk.FinalStateDiff)
feState, err := msh.getFeStateFromDiff(stateDiff)
if origStatePtr == donePk.FinalStateBasePtr {
// this is the normal case. the stateptr from the run-packet should match the baseptr from the done-packet
// this is also the most efficient, because we don't need to fetch the original state
sapi, err := shellapi.MakeShellApi(origState.GetShellType())
if err != nil {
return nil, fmt.Errorf("cannot make shellapi from initial state: %w", err)
}
fullState, err := sapi.ApplyShellStateDiff(origState, stateDiff)
if err != nil {
return nil, fmt.Errorf("cannot apply shell state diff: %w", err)
}
return fullState, nil
}
// this is strange (why is backend returning non-original stateptr?)
// but here, we fetch the stateptr, and then apply the diff against that
realOrigState, err := sstore.GetFullState(ctx, *donePk.FinalStateBasePtr)
if err != nil {
return nil, nil, nil, nil, err
return nil, fmt.Errorf("cannot get original state for diff: %w", err)
}
fullState := msh.StateMap.GetStateByHash(stateDiff.GetShellType(), stateDiff.BaseHash)
if fullState != nil {
sstore.StoreStateBase(ctx, fullState)
if realOrigState == nil {
return nil, fmt.Errorf("cannot get original state for diff: not found")
}
diffHashArr := append(([]string)(nil), donePk.FinalStateDiff.DiffHashArr...)
diffHashArr = append(diffHashArr, donePk.FinalStateDiff.GetHashVal(false))
statePtr := &sstore.ShellStatePtr{BaseHash: donePk.FinalStateDiff.BaseHash, DiffHashArr: diffHashArr}
return statePtr, feState, nil, stateDiff, nil
sapi, err := shellapi.MakeShellApi(realOrigState.GetShellType())
if err != nil {
return nil, fmt.Errorf("cannot make shellapi from original state: %w", err)
}
fullState, err := sapi.ApplyShellStateDiff(realOrigState, stateDiff)
if err != nil {
return nil, fmt.Errorf("cannot apply shell state diff: %w", err)
}
return fullState, nil
}
return nil, nil, nil, nil, nil
return nil, nil
}
// after this limit we'll switch to persisting the full state
const NewStateDiffSizeThreshold = 30 * 1024
// will update the remote instance with the final state
// this is complicated because we want to be as efficient as possible.
// so we pull the current remote-instance state (just the baseptr). then we compute the diff.
// then we check the size of the diff, and only persist the diff it is under some size threshold
// also we check to see if the diff succeeds (it can fail if the shell or version changed).
// in those cases we also update the RI with the full state
func (msh *MShellProc) updateRIWithFinalState(ctx context.Context, rct *RunCmdType, newState *packet.ShellState) (*sstore.RemoteInstance, error) {
curRIState, err := sstore.GetRemoteStatePtr(ctx, rct.SessionId, rct.ScreenId, rct.RemotePtr)
if err != nil {
return nil, fmt.Errorf("error trying to get current screen stateptr: %w", err)
}
feState := sstore.FeStateFromShellState(newState)
if curRIState == nil {
// no current state, so just persist the full state
return sstore.UpdateRemoteState(ctx, rct.SessionId, rct.ScreenId, rct.RemotePtr, feState, newState, nil)
}
// pull the base (not the diff) state from the RI (right now we don't want to make multi-level diffs)
riBaseState, err := sstore.GetStateBase(ctx, curRIState.BaseHash)
if err != nil {
return nil, fmt.Errorf("error trying to get statebase: %w", err)
}
sapi, err := shellapi.MakeShellApi(riBaseState.GetShellType())
if err != nil {
return nil, fmt.Errorf("error trying to make shellapi: %w", err)
}
newStateDiff, err := sapi.MakeShellStateDiff(riBaseState, curRIState.BaseHash, newState)
if err != nil {
// if we can't make a diff, just persist the full state (this could happen if the shell type changes)
return sstore.UpdateRemoteState(ctx, rct.SessionId, rct.ScreenId, rct.RemotePtr, feState, newState, nil)
}
// we have a diff, let's check the diff size first
_, encodedDiff := newStateDiff.EncodeAndHash()
if len(encodedDiff) > NewStateDiffSizeThreshold {
// diff is too large, persist the full state
return sstore.UpdateRemoteState(ctx, rct.SessionId, rct.ScreenId, rct.RemotePtr, feState, newState, nil)
}
// diff is small enough, persist the diff
return sstore.UpdateRemoteState(ctx, rct.SessionId, rct.ScreenId, rct.RemotePtr, feState, nil, newStateDiff)
}
func (msh *MShellProc) handleCmdDonePacket(rct *RunCmdType, donePk *packet.CmdDonePacketType) {
@ -2261,12 +2330,12 @@ func (msh *MShellProc) handleCmdDonePacket(rct *RunCmdType, donePk *packet.CmdDo
// only update DB for non-ephemeral commands
err := sstore.UpdateCmdDoneInfo(ctx, update, donePk.CK, donePk, sstore.CmdStatusDone)
if err != nil {
msh.WriteToPtyBuffer("*error updating cmddone: %v\n", err)
log.Printf("error updating cmddone info (in handleCmdDonePacket): %v\n", err)
return
}
screen, err := sstore.UpdateScreenFocusForDoneCmd(ctx, donePk.CK.GetGroupId(), donePk.CK.GetCmdId())
if err != nil {
msh.WriteToPtyBuffer("*error trying to update screen focus type: %v\n", err)
log.Printf("error trying to update screen focus type (in handleCmdDonePacket): %v\n", err)
// fall-through (nothing to do)
}
if screen != nil {
@ -2274,24 +2343,28 @@ func (msh *MShellProc) handleCmdDonePacket(rct *RunCmdType, donePk *packet.CmdDo
}
}
// ephemeral commands *do* update the remote state
if donePk.FinalState != nil || donePk.FinalStateDiff != nil {
statePtr, feState, finalState, finalStateDiff, err := msh.makeStatePtrFromFinalState(ctx, donePk)
// not all commands get a final state (only RtnState commands have this returned)
// so in those cases finalState will be nil
finalState, err := msh.resolveFinalState(ctx, rct.RunPacket.State, rct.RunPacket.StatePtr, donePk)
if err != nil {
log.Printf("error resolving final state for cmd: %v\n", err)
// fallthrough
}
if finalState != nil {
newRI, err := msh.updateRIWithFinalState(ctx, rct, finalState)
if err != nil {
msh.WriteToPtyBuffer("*error trying to read final command state: %v\n", err)
log.Printf("error updating RI with final state (in handleCmdDonePacket): %v\n", err)
// fallthrough
}
remoteInst, err := sstore.UpdateRemoteState(ctx, rct.SessionId, rct.ScreenId, rct.RemotePtr, feState, finalState, finalStateDiff)
if err != nil {
msh.WriteToPtyBuffer("*error trying to update remotestate: %v\n", err)
// fall-through (nothing to do)
}
if remoteInst != nil {
update.AddUpdate(sstore.MakeSessionUpdateForRemote(rct.SessionId, remoteInst))
if newRI != nil {
update.AddUpdate(sstore.MakeSessionUpdateForRemote(rct.SessionId, newRI))
}
// ephemeral commands *do not* update cmd state (there is no command)
if statePtr != nil && !rct.Ephemeral {
err = sstore.UpdateCmdRtnState(ctx, donePk.CK, *statePtr)
if newRI != nil && !rct.Ephemeral {
newRIStatePtr := packet.ShellStatePtr{BaseHash: newRI.StateBaseHash, DiffHashArr: newRI.StateDiffHashArr}
err = sstore.UpdateCmdRtnState(ctx, donePk.CK, newRIStatePtr)
if err != nil {
msh.WriteToPtyBuffer("*error trying to update cmd rtnstate: %v\n", err)
log.Printf("error trying to update cmd rtnstate: %v\n", err)
// fall-through (nothing to do)
}
}
@ -2605,7 +2678,7 @@ func (msh *MShellProc) getFullState(shellType string, stateDiff *packet.ShellSta
}
return newState, nil
} else {
fullState, err := sstore.GetFullState(context.Background(), sstore.ShellStatePtr{BaseHash: stateDiff.BaseHash, DiffHashArr: stateDiff.DiffHashArr})
fullState, err := sstore.GetFullState(context.Background(), packet.ShellStatePtr{BaseHash: stateDiff.BaseHash, DiffHashArr: stateDiff.DiffHashArr})
if err != nil {
return nil, err
}
@ -2632,7 +2705,7 @@ func (msh *MShellProc) getFeStateFromDiff(stateDiff *packet.ShellStateDiff) (map
}
return sstore.FeStateFromShellState(newState), nil
} else {
fullState, err := sstore.GetFullState(context.Background(), sstore.ShellStatePtr{BaseHash: stateDiff.BaseHash, DiffHashArr: stateDiff.DiffHashArr})
fullState, err := sstore.GetFullState(context.Background(), packet.ShellStatePtr{BaseHash: stateDiff.BaseHash, DiffHashArr: stateDiff.DiffHashArr})
if err != nil {
return nil, err
}

View File

@ -35,10 +35,10 @@ func (rptr *RemotePtrType) GetDisplayName(baseDisplayName string) string {
return name
}
if rptr.Name != "" {
name = name + ":" + rptr.Name
name = name + "#" + rptr.Name
}
if rptr.OwnerId != "" {
name = "@" + rptr.OwnerId + ":" + name
name = "@" + rptr.OwnerId + "#" + name
}
return name
}

View File

@ -793,7 +793,7 @@ func UpdateCmdDoneInfo(ctx context.Context, update *scbus.ModelUpdatePacketType,
return nil
}
func UpdateCmdRtnState(ctx context.Context, ck base.CommandKey, statePtr ShellStatePtr) error {
func UpdateCmdRtnState(ctx context.Context, ck base.CommandKey, statePtr packet.ShellStatePtr) error {
if ck.IsEmpty() {
return fmt.Errorf("cannot update cmdrtnstate, empty ck")
}
@ -1098,7 +1098,7 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update
return update, nil
}
func GetRemoteState(ctx context.Context, sessionId string, screenId string, remotePtr RemotePtrType) (*packet.ShellState, *ShellStatePtr, error) {
func GetRemoteState(ctx context.Context, sessionId string, screenId string, remotePtr RemotePtrType) (*packet.ShellState, *packet.ShellStatePtr, error) {
ssptr, err := GetRemoteStatePtr(ctx, sessionId, screenId, remotePtr)
if err != nil {
return nil, nil, err
@ -1113,8 +1113,8 @@ func GetRemoteState(ctx context.Context, sessionId string, screenId string, remo
return state, ssptr, err
}
func GetRemoteStatePtr(ctx context.Context, sessionId string, screenId string, remotePtr RemotePtrType) (*ShellStatePtr, error) {
var ssptr *ShellStatePtr
func GetRemoteStatePtr(ctx context.Context, sessionId string, screenId string, remotePtr RemotePtrType) (*packet.ShellStatePtr, error) {
var ssptr *packet.ShellStatePtr
txErr := WithTx(ctx, func(tx *TxWrap) error {
ri, err := GetRemoteInstance(tx.Context(), sessionId, screenId, remotePtr)
if err != nil {
@ -1123,7 +1123,7 @@ func GetRemoteStatePtr(ctx context.Context, sessionId string, screenId string, r
if ri == nil {
return nil
}
ssptr = &ShellStatePtr{ri.StateBaseHash, ri.StateDiffHashArr}
ssptr = &packet.ShellStatePtr{ri.StateBaseHash, ri.StateDiffHashArr}
return nil
})
if txErr != nil {
@ -1836,7 +1836,7 @@ func GetStateBaseVersion(ctx context.Context, baseHash string) (string, error) {
})
}
func GetCurStateDiffFromPtr(ctx context.Context, ssPtr *ShellStatePtr) (*packet.ShellStateDiff, error) {
func GetCurStateDiffFromPtr(ctx context.Context, ssPtr *packet.ShellStatePtr) (*packet.ShellStateDiff, error) {
if ssPtr == nil {
return nil, fmt.Errorf("cannot resolve state, empty stateptr")
}
@ -1894,7 +1894,7 @@ func GetStateDiff(ctx context.Context, diffHash string) (*packet.ShellStateDiff,
}
// returns error when not found
func GetFullState(ctx context.Context, ssPtr ShellStatePtr) (*packet.ShellState, error) {
func GetFullState(ctx context.Context, ssPtr packet.ShellStatePtr) (*packet.ShellState, error) {
var state *packet.ShellState
if ssPtr.BaseHash == "" {
return nil, fmt.Errorf("invalid empty basehash")

View File

@ -538,18 +538,6 @@ func (opts TermOpts) Value() (driver.Value, error) {
return quickValueJson(opts)
}
type ShellStatePtr struct {
BaseHash string
DiffHashArr []string
}
func (ssptr *ShellStatePtr) IsEmpty() bool {
if ssptr == nil || ssptr.BaseHash == "" {
return true
}
return false
}
type RemoteInstance struct {
RIId string `json:"riid"`
Name string `json:"name"`
@ -864,27 +852,27 @@ func (r *RemoteType) GetName() string {
}
type CmdType struct {
ScreenId string `json:"screenid"`
LineId string `json:"lineid"`
Remote RemotePtrType `json:"remote"`
CmdStr string `json:"cmdstr"`
RawCmdStr string `json:"rawcmdstr"`
FeState map[string]string `json:"festate"`
StatePtr ShellStatePtr `json:"state"`
TermOpts TermOpts `json:"termopts"`
OrigTermOpts TermOpts `json:"origtermopts"`
Status string `json:"status"`
CmdPid int `json:"cmdpid"`
RemotePid int `json:"remotepid"`
RestartTs int64 `json:"restartts,omitempty"`
DoneTs int64 `json:"donets"`
ExitCode int `json:"exitcode"`
DurationMs int `json:"durationms"`
RunOut []packet.PacketType `json:"runout,omitempty"`
RtnState bool `json:"rtnstate,omitempty"`
RtnStatePtr ShellStatePtr `json:"rtnstateptr,omitempty"`
Remove bool `json:"remove,omitempty"` // not persisted to DB
Restarted bool `json:"restarted,omitempty"` // not persisted to DB
ScreenId string `json:"screenid"`
LineId string `json:"lineid"`
Remote RemotePtrType `json:"remote"`
CmdStr string `json:"cmdstr"`
RawCmdStr string `json:"rawcmdstr"`
FeState map[string]string `json:"festate"`
StatePtr packet.ShellStatePtr `json:"state"`
TermOpts TermOpts `json:"termopts"`
OrigTermOpts TermOpts `json:"origtermopts"`
Status string `json:"status"`
CmdPid int `json:"cmdpid"`
RemotePid int `json:"remotepid"`
RestartTs int64 `json:"restartts,omitempty"`
DoneTs int64 `json:"donets"`
ExitCode int `json:"exitcode"`
DurationMs int `json:"durationms"`
RunOut []packet.PacketType `json:"runout,omitempty"`
RtnState bool `json:"rtnstate,omitempty"`
RtnStatePtr packet.ShellStatePtr `json:"rtnstateptr,omitempty"`
Remove bool `json:"remove,omitempty"` // not persisted to DB
Restarted bool `json:"restarted,omitempty"` // not persisted to DB
}
func (CmdType) GetType() string {