Skip to content

Commit 24b589b

Browse files
committed
Simplify snapshot compress/decompress logic
Compression creates a zipfile with the same path as the snapshot file containing only the snapshot. Decompression can be a bit simpler by also extracting to the same path, and erroring if there are unexpected contents. In retrospect we probably should have just gzip'd the snapshot file, but I think there was some intention to observe the same behavior as RKE1, which used zip files. Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
1 parent e164615 commit 24b589b

1 file changed

Lines changed: 58 additions & 47 deletions

File tree

pkg/etcd/snapshot.go

Lines changed: 58 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -102,91 +102,102 @@ func snapshotDir(config *config.Control, create bool) (string, error) {
102102

103103
// compressSnapshot compresses the given snapshot and provides the
104104
// caller with the path to the file.
105-
func (e *ETCD) compressSnapshot(snapshotDir, snapshotName, snapshotPath string, now time.Time) (string, error) {
106-
logrus.Info("Compressing etcd snapshot file: " + snapshotName)
105+
func (e *ETCD) compressSnapshot(snapshotDir, snapshotFilename string, mtime time.Time) (zipPath string, err error) {
106+
logrus.Info("Compressing etcd snapshot file: " + snapshotFilename)
107+
snapshotPath := filepath.Join(snapshotDir, snapshotFilename)
108+
zipPath = snapshotPath + snapshot.CompressedExtension
107109

108-
zippedSnapshotName := snapshotName + snapshot.CompressedExtension
109-
zipPath := filepath.Join(snapshotDir, zippedSnapshotName)
110+
defer func() {
111+
if err != nil {
112+
os.Remove(zipPath)
113+
}
114+
}()
110115

111-
zf, err := os.Create(zipPath)
116+
sf, err := os.Open(snapshotPath)
112117
if err != nil {
113118
return "", err
114119
}
115-
defer zf.Close()
116-
117-
zipWriter := zip.NewWriter(zf)
118-
defer zipWriter.Close()
120+
defer sf.Close()
119121

120-
uncompressedPath := filepath.Join(snapshotDir, snapshotName)
121-
fileToZip, err := os.Open(uncompressedPath)
122+
fi, err := sf.Stat()
122123
if err != nil {
123-
os.Remove(zipPath)
124124
return "", err
125125
}
126-
defer fileToZip.Close()
127126

128-
info, err := fileToZip.Stat()
127+
of, err := os.Create(zipPath)
129128
if err != nil {
130-
os.Remove(zipPath)
131129
return "", err
132130
}
131+
defer of.Close()
133132

134-
header, err := zip.FileInfoHeader(info)
133+
zw := zip.NewWriter(of)
134+
defer zw.Close()
135+
136+
zfi, err := zip.FileInfoHeader(fi)
135137
if err != nil {
136-
os.Remove(zipPath)
137138
return "", err
138139
}
139140

140-
header.Name = snapshotName
141-
header.Method = zip.Deflate
142-
header.Modified = now
141+
zfi.Name = snapshotFilename
142+
zfi.Method = zip.Deflate
143+
zfi.Modified = mtime
143144

144-
writer, err := zipWriter.CreateHeader(header)
145+
hw, err := zw.CreateHeader(zfi)
145146
if err != nil {
146-
os.Remove(zipPath)
147147
return "", err
148148
}
149-
_, err = io.Copy(writer, fileToZip)
150149

150+
_, err = io.Copy(hw, sf)
151151
return zipPath, err
152152
}
153153

154154
// decompressSnapshot decompresses the given snapshot and provides the caller
155155
// with the full path to the uncompressed snapshot.
156-
func (e *ETCD) decompressSnapshot(snapshotDir, snapshotFile string) (string, error) {
157-
logrus.Info("Decompressing etcd snapshot file: " + snapshotFile)
156+
func (e *ETCD) decompressSnapshot(snapshotDir, snapshotFilename string) (unzipPath string, err error) {
157+
logrus.Info("Decompressing etcd snapshot file: " + snapshotFilename)
158+
snapshotPath := filepath.Join(snapshotDir, snapshotFilename)
159+
unzipPath = strings.TrimSuffix(snapshotPath, snapshot.CompressedExtension)
160+
161+
defer func() {
162+
if err != nil {
163+
os.Remove(unzipPath)
164+
}
165+
}()
158166

159-
r, err := zip.OpenReader(snapshotFile)
167+
sf, err := os.Open(snapshotPath)
160168
if err != nil {
161169
return "", err
162170
}
163-
defer r.Close()
171+
defer sf.Close()
164172

165-
var decompressed *os.File
166-
for _, sf := range r.File {
167-
decompressed, err = os.OpenFile(strings.Replace(sf.Name, snapshot.CompressedExtension, "", -1), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, sf.Mode())
168-
if err != nil {
169-
return "", err
170-
}
173+
fi, err := sf.Stat()
174+
if err != nil {
175+
return "", err
176+
}
171177

172-
//revive:disable-next-line:defer
173-
defer decompressed.Close()
178+
zf, err := zip.NewReader(sf, fi.Size())
179+
if err != nil {
180+
return "", err
181+
}
174182

175-
ss, err := sf.Open()
176-
if err != nil {
177-
return "", err
178-
}
183+
if len(zf.File) != 1 {
184+
return "", errors.New("unexpected compressed etcd snapshot contents")
185+
}
179186

180-
//revive:disable-next-line:defer
181-
defer ss.Close()
187+
cf, err := zf.File[0].Open()
188+
if err != nil {
189+
return "", err
190+
}
191+
defer cf.Close()
182192

183-
if _, err := io.Copy(decompressed, ss); err != nil {
184-
os.Remove(decompressed.Name())
185-
return "", err
186-
}
193+
of, err := os.OpenFile(unzipPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
194+
if err != nil {
195+
return "", err
187196
}
197+
defer of.Close()
188198

189-
return decompressed.Name(), nil
199+
_, err = io.Copy(of, cf)
200+
return unzipPath, err
190201
}
191202

192203
// Snapshot attempts to save a new snapshot to the configured directory, and then clean up any old and failed
@@ -289,7 +300,7 @@ func (e *ETCD) snapshot(ctx context.Context) (_ *managed.SnapshotResult, rerr er
289300
// If the snapshot attempt was successful, sf will be nil as we did not set it to store the error message.
290301
if sf == nil {
291302
if e.config.EtcdSnapshotCompress {
292-
zipPath, err := e.compressSnapshot(snapshotDir, snapshotName, snapshotPath, now)
303+
zipPath, err := e.compressSnapshot(snapshotDir, snapshotName, now)
293304

294305
// ensure that the unncompressed snapshot is cleaned up even if compression fails
295306
if err := os.Remove(snapshotPath); err != nil && !os.IsNotExist(err) {

0 commit comments

Comments
 (0)