Skip to content

Commit 48fe469

Browse files
committed
fix: write the uploaded etcd snapshot atomically
The etcd snapshot upload truncated the snapshot file in place, and removed it when the upload failed. The etcd recovery reads that same file, and it runs after the bootstrap API has already returned success. This way, an upload repeated during a running recovery cut the snapshot short and left a partial etcd data directory behind, and every bootstrap after that got rejected. Write the upload to a temporary file next to the snapshot and move it into place only once it is complete. A running recovery keeps reading the snapshot it started with, and a failed upload removes only its own temporary file. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
1 parent f25b4a3 commit 48fe469

1 file changed

Lines changed: 8 additions & 1 deletion

File tree

internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2026,7 +2026,10 @@ func (s *Server) EtcdRecover(srv machine.MachineService_EtcdRecoverServer) error
20262026
return err
20272027
}
20282028

2029-
snapshot, err := os.OpenFile(constants.EtcdRecoverySnapshotPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o700)
2029+
// The snapshot is written to a temporary file and moved into place only once it is complete,
2030+
// so that the recovery running in the etcd service keeps reading the snapshot it started with:
2031+
// a repeated upload never truncates or removes the file under an in-progress recovery.
2032+
snapshot, err := os.CreateTemp(filepath.Dir(constants.EtcdRecoverySnapshotPath), filepath.Base(constants.EtcdRecoverySnapshotPath)+".*")
20302033
if err != nil {
20312034
return fmt.Errorf("error creating etcd recovery snapshot: %w", err)
20322035
}
@@ -2067,6 +2070,10 @@ func (s *Server) EtcdRecover(srv machine.MachineService_EtcdRecoverServer) error
20672070
return fmt.Errorf("error closing snapshot: %w", err)
20682071
}
20692072

2073+
if err = os.Rename(snapshot.Name(), constants.EtcdRecoverySnapshotPath); err != nil {
2074+
return fmt.Errorf("error moving snapshot into place: %w", err)
2075+
}
2076+
20702077
successfulUpload = true
20712078

20722079
return srv.SendAndClose(&machine.EtcdRecoverResponse{

0 commit comments

Comments
 (0)