Fix every finding surfaced by the canonical .golangci.yml with golangci-lint v2.12.2 (refs #61), behavior-preserving throughout: - err113: dynamic errors replaced with package-level sentinels and %w wrapping; direct comparisons converted to errors.Is - goprintffuncname: printf-style helpers renamed with an f suffix (ui.Writer message methods, cli.ReportErrorf, database.Fatalf, vaultik stdoutf) and all call sites updated - revive: stuttering type names renamed (blob.Handler, blob.WithReader, blob.ChunkPosition, storage.URL, storage.Info), doc comments added, unused parameters blanked, package comments added - contextcheck/noctx: ctx threaded through blob.Packer (AddChunk/Flush/FinalizeBlob/PackChunks) and scanner call sites; context-aware exec and sql variants used - funlen/cyclop/gocognit/nestif/dupl: oversized or duplicated functions split into focused helpers across production and test code - paralleltest/tparallel/thelper/usetesting/testpackage: tests parallelized where safe (global log.Initialize kept in the serial phase), helpers marked, t.TempDir adopted, external test packages where only exported API is used - gosec: integer conversions clamped or justified, header timeouts added, remaining findings suppressed with per-site justifications - mnd/goconst/lll/wsl_v5/nlreturn/noinlineerr/errcheck and other mechanical findings fixed directly Remove the deprecated log.LogOptions alias (callers migrated to log.Options). make check is green.
82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
package snapshot
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/klauspost/compress/zstd"
|
|
)
|
|
|
|
// Manifest represents the structure of a snapshot's blob manifest
|
|
//
|
|
//nolint:tagliatelle // snake_case is the established on-disk manifest format
|
|
type Manifest struct {
|
|
SnapshotID string `json:"snapshot_id"`
|
|
Timestamp string `json:"timestamp"`
|
|
BlobCount int `json:"blob_count"`
|
|
TotalCompressedSize int64 `json:"total_compressed_size"`
|
|
Blobs []BlobInfo `json:"blobs"`
|
|
}
|
|
|
|
// BlobInfo represents information about a single blob in the manifest
|
|
//
|
|
//nolint:tagliatelle // snake_case is the established on-disk manifest format
|
|
type BlobInfo struct {
|
|
Hash string `json:"hash"`
|
|
CompressedSize int64 `json:"compressed_size"`
|
|
}
|
|
|
|
// DecodeManifest decodes a manifest from a reader containing compressed JSON
|
|
func DecodeManifest(r io.Reader) (*Manifest, error) {
|
|
// Decompress using zstd
|
|
zr, err := zstd.NewReader(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating zstd reader: %w", err)
|
|
}
|
|
defer zr.Close()
|
|
|
|
// Decode JSON manifest
|
|
var manifest Manifest
|
|
|
|
err = json.NewDecoder(zr).Decode(&manifest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding manifest: %w", err)
|
|
}
|
|
|
|
return &manifest, nil
|
|
}
|
|
|
|
// EncodeManifest encodes a manifest to compressed JSON
|
|
func EncodeManifest(manifest *Manifest, compressionLevel int) ([]byte, error) {
|
|
// Marshal to JSON
|
|
jsonData, err := json.MarshalIndent(manifest, "", " ")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshaling manifest: %w", err)
|
|
}
|
|
|
|
// Compress using zstd
|
|
var compressedBuf bytes.Buffer
|
|
|
|
writer, err := zstd.NewWriter(&compressedBuf,
|
|
zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(compressionLevel)))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating zstd writer: %w", err)
|
|
}
|
|
|
|
_, err = writer.Write(jsonData)
|
|
if err != nil {
|
|
_ = writer.Close()
|
|
|
|
return nil, fmt.Errorf("writing compressed data: %w", err)
|
|
}
|
|
|
|
err = writer.Close()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("closing zstd writer: %w", err)
|
|
}
|
|
|
|
return compressedBuf.Bytes(), nil
|
|
}
|