Files
kopia/cli/command_snapshot_pin.go
Jarek Kowalski 9bf9cac7fb refactor(repository): ensure we always parse content.ID and object.ID (#1960)
* refactor(repository): ensure we always parse content.ID and object.ID

This changes the types to be incompatible with string to prevent direct
conversion to and from string.

This has the additional benefit of reducing number of memory allocations
and bytes for all IDs.

content.ID went from 2 allocations to 1:
   typical case 32 characters + 16 bytes per-string overhead
   worst-case 65 characters + 16 bytes per-string overhead
   now: 34 bytes

object.ID went from 2 allocations to 1:
   typical case 32 characters + 16 bytes per-string overhead
   worst-case 65 characters + 16 bytes per-string overhead
   now: 36 bytes

* move index.{ID,IDRange} methods to separate files

* replaced index.IDFromHash with content.IDFromHash externally

* minor tweaks and additional tests

* Update repo/content/index/id_test.go

Co-authored-by: Julio Lopez <1953782+julio-lopez@users.noreply.github.com>

* Update repo/content/index/id_test.go

Co-authored-by: Julio Lopez <1953782+julio-lopez@users.noreply.github.com>

* pr feedback

* post-merge fixes

* pr feedback

* pr feedback

* fixed subtle regression in sortedContents()

This was actually not producing invalid results because of how base36
works, just not sorting as efficiently as it could.

Co-authored-by: Julio Lopez <1953782+julio-lopez@users.noreply.github.com>
2022-05-25 14:15:56 +00:00

84 lines
2.5 KiB
Go

package cli
import (
"context"
"github.com/pkg/errors"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/manifest"
"github.com/kopia/kopia/repo/object"
"github.com/kopia/kopia/snapshot"
)
type commandSnapshotPin struct {
addPins []string
removePins []string
snapshotIDs []string
}
func (c *commandSnapshotPin) setup(svc appServices, parent commandParent) {
cmd := parent.Command("pin", "Add or remove pins preventing snapshot deletion")
cmd.Flag("add", "Add pins").StringsVar(&c.addPins)
cmd.Flag("remove", "Remove pins").StringsVar(&c.removePins)
cmd.Arg("id", "Snapshot ID or root object ID").Required().StringsVar(&c.snapshotIDs)
cmd.Action(svc.repositoryWriterAction(c.run))
}
func (c *commandSnapshotPin) run(ctx context.Context, rep repo.RepositoryWriter) error {
if len(c.addPins)+len(c.removePins) == 0 {
return errors.Errorf("must specify --add and/or --remove")
}
for _, id := range c.snapshotIDs {
m, err := snapshot.LoadSnapshot(ctx, rep, manifest.ID(id))
if err == nil {
if err = c.pinSnapshot(ctx, rep, m); err != nil {
return errors.Wrapf(err, "error pinning %v", id)
}
} else if !errors.Is(err, snapshot.ErrSnapshotNotFound) {
return errors.Wrapf(err, "error loading snapshot %v", id)
} else if err := c.pinSnapshotsByRootObjectID(ctx, rep, id); err != nil {
return errors.Wrapf(err, "error pinning snapshots by root ID %v", id)
}
}
return nil
}
func (c *commandSnapshotPin) pinSnapshotsByRootObjectID(ctx context.Context, rep repo.RepositoryWriter, rootID string) error {
rootOID, err := object.ParseID(rootID)
if err != nil {
return errors.Wrap(err, "unable to parse object ID")
}
manifests, err := snapshot.FindSnapshotsByRootObjectID(ctx, rep, rootOID)
if err != nil {
return errors.Wrapf(err, "unable to find snapshots by root %v", rootID)
}
if len(manifests) == 0 {
return errors.Errorf("no snapshots matched %v", rootID)
}
for _, m := range manifests {
if err := c.pinSnapshot(ctx, rep, m); err != nil {
return errors.Wrap(err, "error pinning")
}
}
return nil
}
func (c *commandSnapshotPin) pinSnapshot(ctx context.Context, rep repo.RepositoryWriter, m *snapshot.Manifest) error {
if !m.UpdatePins(c.addPins, c.removePins) {
log(ctx).Infof("No change for snapshot at %v of %v", formatTimestamp(m.StartTime), m.Source)
return nil
}
log(ctx).Infof("Updating snapshot at %v of %v", formatTimestamp(m.StartTime), m.Source)
return errors.Wrap(snapshot.UpdateSnapshot(ctx, rep, m), "error updating snapshot")
}