Files
soft_quay/core/updater/transaction.go
T

88 lines
2.6 KiB
Go

package updater
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
)
type transactionPhase string
const (
phasePrepared transactionPhase = "prepared"
phaseTargetBackedUp transactionPhase = "target_backed_up"
phaseStagingActivated transactionPhase = "staging_activated"
phaseLaunched transactionPhase = "launched"
phaseCommitted transactionPhase = "committed"
)
type transaction struct {
SchemaVersion int `json:"schema_version"`
RequestID string `json:"request_id"`
Phase transactionPhase `json:"phase"`
}
func (record transaction) validate() error {
if record.SchemaVersion != transactionSchemaVersion || !validRequestID(record.RequestID) {
return ErrTransactionCorrupt
}
switch record.Phase {
case phasePrepared, phaseTargetBackedUp, phaseStagingActivated, phaseLaunched, phaseCommitted:
return nil
default:
return ErrTransactionCorrupt
}
}
func writeTransaction(layout updateLayout, phase transactionPhase, syncer DirectorySyncer) error {
record := transaction{SchemaVersion: transactionSchemaVersion, RequestID: layout.requestID, Phase: phase}
if err := record.validate(); err != nil {
return err
}
data, err := json.Marshal(record)
if err != nil {
return fmt.Errorf("encode self-update transaction: %w", err)
}
data = append(data, '\n')
if err := replaceRegularFile(layout.root, layout.transaction, ".self-update-transaction-*.tmp", data, syncer); err != nil {
return fmt.Errorf("write self-update transaction: %w", err)
}
return nil
}
func loadTransaction(layout updateLayout) (transaction, bool, error) {
data, err := readRegularFile(layout.transaction)
if os.IsNotExist(err) {
return transaction{}, false, nil
}
if err != nil {
return transaction{}, false, fmt.Errorf("read self-update transaction: %w", err)
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
var record transaction
if err := decoder.Decode(&record); err != nil {
return transaction{}, false, fmt.Errorf("%w: %v", ErrTransactionCorrupt, err)
}
var extra interface{}
if err := decoder.Decode(&extra); err != io.EOF {
if err == nil {
return transaction{}, false, fmt.Errorf("%w: trailing JSON value", ErrTransactionCorrupt)
}
return transaction{}, false, fmt.Errorf("%w: trailing data: %v", ErrTransactionCorrupt, err)
}
if err := record.validate(); err != nil {
return transaction{}, false, err
}
return record, true, nil
}
func removeTransaction(layout updateLayout, syncer DirectorySyncer) error {
if err := removeRegularFile(layout.transaction, layout.root, syncer); err != nil {
return fmt.Errorf("remove self-update transaction: %w", err)
}
return nil
}