Remove obsolete V2 snapshot files
parent
ad3a29de16
commit
3ff1d87120
@ -1,154 +0,0 @@
|
|||||||
package snapshot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
RqliteHeaderVersionSize = 32
|
|
||||||
RqliteHeaderReservedSize = 32
|
|
||||||
|
|
||||||
RqliteSnapshotVersion2 = "rqlite snapshot version 2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileIsV2Snapshot returns true if the given path is a V2 snapshot.
|
|
||||||
func FileIsV2Snapshot(path string) bool {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
return ReaderIsV2Snapshot(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReaderIsV2Snapshot returns true if the given reader is a V2 snapshot.
|
|
||||||
// The reader will be advanced 1 byte passed the end of the Version header.
|
|
||||||
func ReaderIsV2Snapshot(r io.Reader) bool {
|
|
||||||
header := make([]byte, RqliteHeaderVersionSize)
|
|
||||||
if _, err := io.ReadFull(r, header); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return string(header[:len(RqliteSnapshotVersion2)]) == RqliteSnapshotVersion2
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2Encoder creates a new V2 snapshot.
|
|
||||||
type V2Encoder struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV2Encoder returns an initialized V2 encoder
|
|
||||||
func NewV2Encoder(path string) *V2Encoder {
|
|
||||||
return &V2Encoder{
|
|
||||||
path: path,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteTo writes the snapshot to the given writer. Returns the number
|
|
||||||
// of bytes written, or an error.
|
|
||||||
func (v *V2Encoder) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
file, err := os.Open(v.path)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
// Wrap w in counting writer.
|
|
||||||
cw := &CountingWriter{Writer: w}
|
|
||||||
|
|
||||||
if _, err := writeString(cw, RqliteSnapshotVersion2, RqliteHeaderVersionSize); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write reserved space.
|
|
||||||
if _, err = cw.Write(make([]byte, RqliteHeaderReservedSize)); err != nil {
|
|
||||||
return cw.Count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
gw, err := gzip.NewWriterLevel(cw, gzip.BestSpeed)
|
|
||||||
if err != nil {
|
|
||||||
return cw.Count, err
|
|
||||||
}
|
|
||||||
defer gw.Close()
|
|
||||||
|
|
||||||
if _, err := io.Copy(gw, file); err != nil {
|
|
||||||
return cw.Count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're done.
|
|
||||||
if err := gw.Close(); err != nil {
|
|
||||||
return cw.Count, err
|
|
||||||
}
|
|
||||||
if err := file.Close(); err != nil {
|
|
||||||
return cw.Count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cw.Count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2Decoder reads a V2 snapshot.
|
|
||||||
type V2Decoder struct {
|
|
||||||
r io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV2Decoder returns an initialized V2 decoder
|
|
||||||
func NewV2Decoder(r io.Reader) *V2Decoder {
|
|
||||||
return &V2Decoder{
|
|
||||||
r: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteTo writes the decoded snapshot data to the given writer.
|
|
||||||
func (v *V2Decoder) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
if !ReaderIsV2Snapshot(v.r) {
|
|
||||||
return 0, fmt.Errorf("data is not a V2 snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the reserved space and discard.
|
|
||||||
reserved := make([]byte, RqliteHeaderReservedSize)
|
|
||||||
if _, err := io.ReadFull(v.r, reserved); err != nil {
|
|
||||||
return 0, fmt.Errorf("failed to read reserved space: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gr, err := gzip.NewReader(v.r)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer gr.Close()
|
|
||||||
|
|
||||||
// Decompress the database.
|
|
||||||
n, err := io.Copy(w, gr)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("failed to write data: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// function which takes a writer, a string, and a length. If the string is longer
|
|
||||||
// than the length return an error. Otherwise string the string to the writer and
|
|
||||||
// fil the remain space up to the lnegth with 0.
|
|
||||||
func writeString(w io.Writer, s string, l int) (int, error) {
|
|
||||||
if len(s) >= l {
|
|
||||||
return 0, fmt.Errorf("string too long (%d, %d)", len(s), l)
|
|
||||||
}
|
|
||||||
if _, err := w.Write([]byte(s)); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return w.Write(make([]byte, l-len(s)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountingWriter counts the number of bytes written to it.
|
|
||||||
type CountingWriter struct {
|
|
||||||
Writer io.Writer
|
|
||||||
Count int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes to the underlying writer and counts the number of bytes written.
|
|
||||||
func (cw *CountingWriter) Write(p []byte) (int, error) {
|
|
||||||
n, err := cw.Writer.Write(p)
|
|
||||||
cw.Count += int64(n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
package snapshot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test_V1Encoder_WriteTo tests that the V1Encoder.WriteTo method
|
|
||||||
// writes a valid Snapshot to the given io.Writer.
|
|
||||||
func Test_V2Encoder_WriteTo(t *testing.T) {
|
|
||||||
testFilePath := makeTempFile(t)
|
|
||||||
defer os.Remove(testFilePath)
|
|
||||||
|
|
||||||
// Create V2Encoder with a test file path
|
|
||||||
v := NewV2Encoder(testFilePath)
|
|
||||||
|
|
||||||
// Create a buffer to serve as the io.Writer
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
// Write a snapshot to the buffer
|
|
||||||
_, err := v.WriteTo(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error in WriteTo: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a reader from the buffer
|
|
||||||
r := bytes.NewReader(buf.Bytes())
|
|
||||||
|
|
||||||
// Now sanity check the snapshot.
|
|
||||||
if !ReaderIsV2Snapshot(r) {
|
|
||||||
t.Fatalf("ReaderIsV2Snapshot returned false for valid snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the Snapshot to a temp file.
|
|
||||||
tempSnapshotPath := makeTempFile(t)
|
|
||||||
defer os.Remove(tempSnapshotPath)
|
|
||||||
|
|
||||||
if err := os.WriteFile(tempSnapshotPath, buf.Bytes(), 0644); err != nil {
|
|
||||||
t.Fatalf("Error writing temp file: %v", err)
|
|
||||||
}
|
|
||||||
if !FileIsV2Snapshot(tempSnapshotPath) {
|
|
||||||
t.Fatalf("FileIsV2Snapshot returned false for valid snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test_V1Encoder_WriteToNoFile tests that the V1Encoder.WriteTo method
|
|
||||||
// returns an error when the given file does not exist.
|
|
||||||
func Test_V2Encoder_WriteToNoFile(t *testing.T) {
|
|
||||||
v := NewV2Encoder("/does/not/exist")
|
|
||||||
_, err := v.WriteTo(new(bytes.Buffer))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected error in WriteTo due to non-existent file, but got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_V2SnapshotEncodeDecode(t *testing.T) {
|
|
||||||
f, err := os.CreateTemp(t.TempDir(), "test-file")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating temp file: %v", err)
|
|
||||||
}
|
|
||||||
const size = 1024
|
|
||||||
_, err = io.CopyN(f, rand.Reader, size)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
// Encode it as a v2 snapshot to a byte buffer.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
enc := NewV2Encoder(f.Name())
|
|
||||||
n, err := enc.WriteTo(&buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that `n` matches the number of bytes in the buffer.
|
|
||||||
if n != int64(buf.Len()) {
|
|
||||||
t.Fatalf("expected %d bytes, got %d", n, buf.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass the byte buffer to a decoder.
|
|
||||||
dec := NewV2Decoder(&buf)
|
|
||||||
|
|
||||||
// Have it decode the snapshot to a second byte buffer.
|
|
||||||
var decodeBuf bytes.Buffer
|
|
||||||
_, err = dec.WriteTo(&decodeBuf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that we get the original contents back.
|
|
||||||
f, err = os.Open(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
var originalBuf bytes.Buffer
|
|
||||||
_, err = io.Copy(&originalBuf, f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(originalBuf.Bytes(), decodeBuf.Bytes()) {
|
|
||||||
t.Fatal("original file content and decoded content are not the same")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeTempFile(t *testing.T) string {
|
|
||||||
f, err := os.CreateTemp(t.TempDir(), "test-file")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating temp file: %v", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
return f.Name()
|
|
||||||
}
|
|
Loading…
Reference in New Issue