feat: complete Windows client control and recovery paths
This commit is contained in:
@@ -50,6 +50,7 @@ type EventAppend struct {
|
||||
ImmutableSHA256 [32]byte
|
||||
Lifecycle *rvboxv1.CommandLifecycle
|
||||
LifecycleRevision uint64
|
||||
WindowsIdentity *rvboxv1.WindowsExecutionIdentity
|
||||
Output bool
|
||||
UseCloseout bool
|
||||
}
|
||||
@@ -79,6 +80,16 @@ func (store *Store) AppendCommandEvent(ctx context.Context, event EventAppend) (
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
var windowsIdentity []byte
|
||||
if event.WindowsIdentity != nil {
|
||||
if len(event.WindowsIdentity.GetAttemptedContexts()) > 8 || len(event.WindowsIdentity.GetSelectionDetail()) > 4096 {
|
||||
return result, ErrInvalidSegmentRecord
|
||||
}
|
||||
windowsIdentity, err = proto.MarshalOptions{Deterministic: true}.Marshal(event.WindowsIdentity)
|
||||
if err != nil || len(windowsIdentity) > 16<<10 {
|
||||
return result, ErrInvalidSegmentRecord
|
||||
}
|
||||
}
|
||||
|
||||
store.writeMu.Lock()
|
||||
defer store.writeMu.Unlock()
|
||||
@@ -89,10 +100,13 @@ func (store *Store) AppendCommandEvent(ctx context.Context, event EventAppend) (
|
||||
var lastSequence, commandOutputCharged, commandCharged, clientCharged, serverCharged, closeoutRemaining uint64
|
||||
var currentLifecycle uint32
|
||||
var currentRevision uint64
|
||||
var lateAfterExpiry int
|
||||
var queueExpiry sql.NullInt64
|
||||
var clientID string
|
||||
query := `SELECT commands.last_event_seq, commands.output_charged_bytes,
|
||||
commands.charged_bytes, commands.closeout_remaining_bytes, commands.client_id, clients.charged_bytes,
|
||||
storage_counters.command_charged_bytes, commands.lifecycle, commands.revision
|
||||
storage_counters.command_charged_bytes, commands.lifecycle, commands.revision,
|
||||
commands.queue_expiry_time, commands.late_after_expiry
|
||||
FROM commands JOIN clients ON clients.client_id = commands.client_id
|
||||
JOIN storage_counters ON storage_counters.singleton = 1 WHERE commands.issue_uuid = ?`
|
||||
arguments := []any{event.IssueUUID[:]}
|
||||
@@ -105,7 +119,7 @@ JOIN storage_counters ON storage_counters.singleton = 1 WHERE commands.issue_uui
|
||||
arguments = append(arguments, event.SessionGeneration)
|
||||
}
|
||||
err = database.QueryRowContext(ctx, query, arguments...).Scan(
|
||||
&lastSequence, &commandOutputCharged, &commandCharged, &closeoutRemaining, &clientID, &clientCharged, &serverCharged, ¤tLifecycle, ¤tRevision)
|
||||
&lastSequence, &commandOutputCharged, &commandCharged, &closeoutRemaining, &clientID, &clientCharged, &serverCharged, ¤tLifecycle, ¤tRevision, &queueExpiry, &lateAfterExpiry)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return result, ErrCommandNotFound
|
||||
}
|
||||
@@ -133,6 +147,11 @@ JOIN storage_counters ON storage_counters.singleton = 1 WHERE commands.issue_uui
|
||||
return result, domain.ValidateTransition(rvboxv1.CommandLifecycle(currentLifecycle), *event.Lifecycle)
|
||||
}
|
||||
}
|
||||
late := queueExpiry.Valid && event.ReceiptUnixNano >= queueExpiry.Int64
|
||||
lateValue := 0
|
||||
if late {
|
||||
lateValue = 1
|
||||
}
|
||||
rowsCharged, indexesCharged := uint64(1), uint64(1)
|
||||
if store.willCreateSegment(event.IssueUUID, uint64(len(encoded))) {
|
||||
rowsCharged++
|
||||
@@ -215,8 +234,12 @@ payload, segment_ordinal, segment_record_offset, segment_record_length, immutabl
|
||||
var update sql.Result
|
||||
commandUpdate := `UPDATE commands SET last_event_seq = ?,
|
||||
retained_compressed_bytes = retained_compressed_bytes + ?, output_charged_bytes = ?, charged_bytes = ?,
|
||||
closeout_remaining_bytes = ?`
|
||||
commandArgs := []any{event.EventSeq, len(event.Payload), reservation.CommandOutputCharged, reservation.CommandTotalCharged, reservation.CloseoutRemaining}
|
||||
closeout_remaining_bytes = ?, late_after_expiry = CASE WHEN ? = 1 THEN 1 ELSE late_after_expiry END`
|
||||
commandArgs := []any{event.EventSeq, len(event.Payload), reservation.CommandOutputCharged, reservation.CommandTotalCharged, reservation.CloseoutRemaining, lateValue}
|
||||
if event.WindowsIdentity != nil {
|
||||
commandUpdate = `UPDATE commands SET windows_execution_identity = ?, ` + commandUpdate[len("UPDATE commands SET "):]
|
||||
commandArgs = append([]any{windowsIdentity}, commandArgs...)
|
||||
}
|
||||
if event.Lifecycle != nil {
|
||||
terminal := 0
|
||||
if domain.IsTerminal(*event.Lifecycle) {
|
||||
@@ -236,6 +259,11 @@ retained_compressed_bytes = retained_compressed_bytes + ?, output_charged_bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil && late && lateAfterExpiry == 0 {
|
||||
if err = appendLateExpiryAudit(ctx, tx, event.ReceiptUnixNano, clientID, event.IssueUUID, event.ReceiptUnixNano); err == nil {
|
||||
lateAfterExpiry = 1
|
||||
}
|
||||
}
|
||||
if err == nil && event.EventType == 7 {
|
||||
// Stdin acknowledgements are command events, but their durable delivery
|
||||
// cursor lives in stdin_writes. Mark the cumulative prefix in the same
|
||||
|
||||
Reference in New Issue
Block a user