Authorize comparison replacement at commit time
This commit is contained in:
@@ -51,9 +51,17 @@ type DestinationPlan struct {
|
||||
WorkingDirectory string
|
||||
Target string
|
||||
Replace bool
|
||||
Exists bool
|
||||
state destinationState
|
||||
}
|
||||
|
||||
type destinationState uint8
|
||||
|
||||
const (
|
||||
destinationAbsent destinationState = iota
|
||||
destinationEmpty
|
||||
destinationBundle
|
||||
)
|
||||
|
||||
// ErrUnrecognizedBundle marks a directory that is not a valid current-schema
|
||||
// Weatherreporter comparison bundle.
|
||||
var ErrUnrecognizedBundle = errors.New("unrecognized comparison bundle")
|
||||
@@ -98,7 +106,7 @@ func PlanDestination(workingDirectory, target string, replace bool) (Destination
|
||||
return DestinationPlan{}, newDestinationError(DestinationInspection, target, err)
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
plan.Exists = true
|
||||
plan.state = destinationEmpty
|
||||
return plan, nil
|
||||
}
|
||||
if !replace {
|
||||
@@ -107,7 +115,7 @@ func PlanDestination(workingDirectory, target string, replace bool) (Destination
|
||||
if _, err := RecognizeBundle(target); err != nil {
|
||||
return DestinationPlan{}, newDestinationError(DestinationUnrecognized, target, err)
|
||||
}
|
||||
plan.Exists = true
|
||||
plan.state = destinationBundle
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
@@ -312,7 +320,7 @@ func publish(ctx context.Context, plan DestinationPlan, bundle LogicalBundle, op
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !currentPlan.Exists {
|
||||
if currentPlan.state == destinationAbsent {
|
||||
if err := operations.rename(temporaryDirectory, currentPlan.Target); err != nil {
|
||||
return fmt.Errorf("publish comparison bundle to %q: %w", currentPlan.Target, err)
|
||||
}
|
||||
@@ -327,15 +335,11 @@ func publish(ctx context.Context, plan DestinationPlan, bundle LogicalBundle, op
|
||||
if err := operations.rename(currentPlan.Target, backupDirectory); err != nil {
|
||||
return fmt.Errorf("back up comparison destination %q: %w", currentPlan.Target, err)
|
||||
}
|
||||
if err := authorizeMovedDestination(currentPlan, backupDirectory); err != nil {
|
||||
return restoreMovedDestination(operations, backupDirectory, currentPlan.Target, err)
|
||||
}
|
||||
if err := operations.rename(temporaryDirectory, currentPlan.Target); err != nil {
|
||||
restoreErr := operations.rename(backupDirectory, currentPlan.Target)
|
||||
if restoreErr != nil {
|
||||
return errors.Join(
|
||||
fmt.Errorf("replace comparison destination %q: %w", currentPlan.Target, err),
|
||||
fmt.Errorf("restore prior comparison destination from %q: %w", backupDirectory, restoreErr),
|
||||
)
|
||||
}
|
||||
return fmt.Errorf("replace comparison destination %q: %w", currentPlan.Target, err)
|
||||
return restoreMovedDestination(operations, backupDirectory, currentPlan.Target, fmt.Errorf("replace comparison destination %q: %w", currentPlan.Target, err))
|
||||
}
|
||||
temporaryDirectory = ""
|
||||
if err := os.RemoveAll(backupDirectory); err != nil {
|
||||
@@ -344,6 +348,38 @@ func publish(ctx context.Context, plan DestinationPlan, bundle LogicalBundle, op
|
||||
return nil
|
||||
}
|
||||
|
||||
func authorizeMovedDestination(plan DestinationPlan, backupDirectory string) error {
|
||||
backupPlan, err := PlanDestination(plan.WorkingDirectory, backupDirectory, plan.Replace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("authorize moved comparison destination %q: %w", backupDirectory, err)
|
||||
}
|
||||
if backupPlan.state != destinationEmpty && backupPlan.state != destinationBundle {
|
||||
return fmt.Errorf("authorize moved comparison destination %q: destination disappeared", backupDirectory)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreMovedDestination(operations publishOperations, backupDirectory, target string, cause error) error {
|
||||
if _, err := os.Lstat(target); err == nil {
|
||||
return errors.Join(
|
||||
cause,
|
||||
fmt.Errorf("restore prior comparison destination from %q: destination %q reappeared", backupDirectory, target),
|
||||
)
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return errors.Join(
|
||||
cause,
|
||||
fmt.Errorf("inspect comparison destination %q before restoring from %q: %w", target, backupDirectory, err),
|
||||
)
|
||||
}
|
||||
if restoreErr := operations.rename(backupDirectory, target); restoreErr != nil {
|
||||
return errors.Join(
|
||||
cause,
|
||||
fmt.Errorf("restore prior comparison destination from %q: %w", backupDirectory, restoreErr),
|
||||
)
|
||||
}
|
||||
return cause
|
||||
}
|
||||
|
||||
func writeLogicalBundle(ctx context.Context, directory string, bundle LogicalBundle, manifestData []byte) error {
|
||||
if err := writeBundleFile(ctx, filepath.Join(directory, DataPackageFilename), bundle.DataPackage); err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user