85 lines
3.2 KiB
Go
85 lines
3.2 KiB
Go
package deployment
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// RecoveryDispositionSummary 是本机查询的明确输出边界,不包含请求键、绑定摘要或操作者凭据。
|
|
type RecoveryDispositionSummary struct {
|
|
SourceDispositionID string `json:"source_disposition_id,omitempty"`
|
|
ID string `json:"id"`
|
|
TransactionID string `json:"transaction_id"`
|
|
ServerID string `json:"server_id"`
|
|
Action RecoveryAction `json:"action"`
|
|
Status string `json:"status"`
|
|
ResultCode string `json:"result_code"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
func (r RecoveryDisposition) Summary() RecoveryDispositionSummary {
|
|
return RecoveryDispositionSummary{
|
|
SourceDispositionID: r.Intent.SourceDispositionID,
|
|
ID: r.Intent.ID, TransactionID: r.Intent.TransactionID, ServerID: r.Intent.ServerID,
|
|
Action: r.Intent.Action, Status: r.Status, ResultCode: r.ResultCode,
|
|
CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
func validDispositionID(id string) bool {
|
|
return validDigest(id) && id == strings.ToLower(id) && id != strings.Repeat("0", 64)
|
|
}
|
|
|
|
func (r RecoveryDispositionSummary) Validate() error {
|
|
if isRecoveryVerification(r.Action) != (r.SourceDispositionID != "") || (r.SourceDispositionID != "" && (!validDispositionID(r.SourceDispositionID) || r.SourceDispositionID == r.ID)) {
|
|
return ErrRecoveryDispositionInvalid
|
|
}
|
|
if !validDispositionID(r.ID) || !validInspectionID(r.TransactionID) || !validRecoverySummary(r.ServerID) ||
|
|
!validRecoveryAction(r.Action) || r.CreatedAt.IsZero() || r.UpdatedAt.IsZero() || r.UpdatedAt.Before(r.CreatedAt) {
|
|
return ErrRecoveryDispositionInvalid
|
|
}
|
|
intent := RecoveryDispositionIntent{Action: r.Action}
|
|
switch r.Status {
|
|
case "running":
|
|
if r.ResultCode == "" {
|
|
return nil
|
|
}
|
|
case "succeeded":
|
|
if r.ResultCode != RecoveryResultRejected && r.ResultCode != RecoveryResultUncertain && intent.AllowsResult(r.ResultCode) {
|
|
return nil
|
|
}
|
|
case RecoveryResultRejected, RecoveryResultUncertain:
|
|
if r.ResultCode == r.Status {
|
|
return nil
|
|
}
|
|
}
|
|
return ErrRecoveryDispositionInvalid
|
|
}
|
|
|
|
// GetRecoveryDisposition 不取得写操作门,执行中或重启后均可查询;查询本身不会重放动作或释放占用。
|
|
func (s *Service) GetRecoveryDisposition(ctx context.Context, id string) (RecoveryDispositionSummary, error) {
|
|
if !validDispositionID(id) {
|
|
return RecoveryDispositionSummary{}, ErrRecoveryDispositionInvalid
|
|
}
|
|
if s == nil {
|
|
return RecoveryDispositionSummary{}, ErrRecoveryDispositionUnavailable
|
|
}
|
|
journal, ok := s.journal.(RecoveryDispositionJournal)
|
|
if !ok {
|
|
return RecoveryDispositionSummary{}, ErrRecoveryDispositionUnavailable
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer cancel()
|
|
item, err := journal.GetRecoveryDisposition(ctx, id)
|
|
if err != nil || ctx.Err() != nil || item.Intent.ID != id || item.Intent.Validate() != nil {
|
|
return RecoveryDispositionSummary{}, ErrRecoveryDispositionUnavailable
|
|
}
|
|
summary := item.Summary()
|
|
if summary.Validate() != nil {
|
|
return RecoveryDispositionSummary{}, ErrRecoveryDispositionUnavailable
|
|
}
|
|
return summary, nil
|
|
}
|