Files
kopia/cli/command_blob_list.go
Jarek Kowalski 03da9b1f0e feat(cli): improved safety of v1->v2 index format upgrade (#2223)
When upgrading from legacy to epoch manager-based index, we will write
an intentionally-corrupted index blob, such that old clients won't be
able to understand it when they read the repository index using legacy
format.

The error message emitted by very old clients is not great, but it's
safer to do that rather than corrupt the repository.

Note that this additional safety has a delay of up to 15 minutes
which is the time required for old clients to stop relying on index list
cache in case of very long-running snapshots, server or KopiaUI.
2022-07-28 17:32:31 -07:00

93 lines
2.1 KiB
Go

package cli
import (
"context"
"strings"
"github.com/kopia/kopia/internal/epoch"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/blob"
"github.com/kopia/kopia/repo/content"
)
type commandBlobList struct {
blobListPrefix string
blobListPrefixExclude []string
blobListMinSize int64
blobListMaxSize int64
dataOnly bool
jo jsonOutput
out textOutput
}
func (c *commandBlobList) setup(svc appServices, parent commandParent) {
cmd := parent.Command("list", "List BLOBs").Alias("ls")
cmd.Flag("prefix", "Blob ID prefix").StringVar(&c.blobListPrefix)
cmd.Flag("exclude-prefix", "Blob ID prefixes to exclude").StringsVar(&c.blobListPrefixExclude)
cmd.Flag("min-size", "Minimum size").Int64Var(&c.blobListMinSize)
cmd.Flag("max-size", "Maximum size").Int64Var(&c.blobListMaxSize)
cmd.Flag("data-only", "Only list data blobs").BoolVar(&c.dataOnly)
c.jo.setup(svc, cmd)
c.out.setup(svc)
cmd.Action(svc.directRepositoryReadAction(c.run))
}
func (c *commandBlobList) run(ctx context.Context, rep repo.DirectRepository) error {
var jl jsonList
jl.begin(&c.jo)
defer jl.end()
// nolint:wrapcheck
return rep.BlobReader().ListBlobs(ctx, blob.ID(c.blobListPrefix), func(b blob.Metadata) error {
if !c.shouldInclude(b) {
return nil
}
if c.jo.jsonOutput {
jl.emit(b)
} else {
c.out.printStdout("%-70v %10v %v\n", b.BlobID, b.Length, formatTimestamp(b.Timestamp))
}
return nil
})
}
func (c *commandBlobList) shouldInclude(b blob.Metadata) bool {
if c.dataOnly {
if strings.HasPrefix(string(b.BlobID), content.LegacyIndexBlobPrefix) {
return false
}
if strings.HasPrefix(string(b.BlobID), epoch.EpochManagerIndexUberPrefix) {
return false
}
if strings.HasPrefix(string(b.BlobID), content.TextLogBlobPrefix) {
return false
}
if strings.HasPrefix(string(b.BlobID), "kopia.") {
return false
}
}
if c.blobListMaxSize != 0 && b.Length > c.blobListMaxSize {
return false
}
if c.blobListMinSize != 0 && b.Length < c.blobListMinSize {
return false
}
for _, ex := range c.blobListPrefixExclude {
if strings.HasPrefix(string(b.BlobID), ex) {
return false
}
}
return true
}