Files
kopia/cli/command_repository_create.go
Jarek Kowalski d84c884321 Added content manager internal logging (#1116)
* logging: added logger wrappers for Broadcast and Prefix

* nit: moved max hash size to a named constant

* content: added internal logger

* content: replaced context-based logging with explicit Loggers

This will capture the logger.Logger associated with the context when
the repository is opened and will reuse it for all logs instead of
creating new logger for each log message.

The new logger will also write logs to the internal logger in addition
to writing to a log file/console.

* cli: allow decrypting all blobs whose names start with _

* maintenance: added logs cleanup

* cli: commands to view logs

* cli: log selected command on each write session
2021-06-05 08:48:43 -07:00

153 lines
5.0 KiB
Go

package cli
import (
"context"
"github.com/alecthomas/kingpin"
"github.com/pkg/errors"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/blob"
"github.com/kopia/kopia/repo/content"
"github.com/kopia/kopia/repo/encryption"
"github.com/kopia/kopia/repo/hashing"
"github.com/kopia/kopia/repo/object"
"github.com/kopia/kopia/repo/splitter"
"github.com/kopia/kopia/snapshot/policy"
)
type commandRepositoryCreate struct {
createBlockHashFormat string
createBlockEncryptionFormat string
createSplitter string
createOnly bool
createIndexVersion int
co connectOptions
svc advancedAppServices
out textOutput
}
func (c *commandRepositoryCreate) setup(svc advancedAppServices, parent commandParent) {
cmd := parent.Command("create", "Create new repository in a specified location.")
cmd.Flag("block-hash", "Content hash algorithm.").PlaceHolder("ALGO").Default(hashing.DefaultAlgorithm).EnumVar(&c.createBlockHashFormat, hashing.SupportedAlgorithms()...)
cmd.Flag("encryption", "Content encryption algorithm.").PlaceHolder("ALGO").Default(encryption.DefaultAlgorithm).EnumVar(&c.createBlockEncryptionFormat, encryption.SupportedAlgorithms(false)...)
cmd.Flag("object-splitter", "The splitter to use for new objects in the repository").Default(splitter.DefaultAlgorithm).EnumVar(&c.createSplitter, splitter.SupportedAlgorithms()...)
cmd.Flag("create-only", "Create repository, but don't connect to it.").Short('c').BoolVar(&c.createOnly)
cmd.Flag("index-version", "Force particular index version").Hidden().Envar("KOPIA_CREATE_INDEX_VERSION").IntVar(&c.createIndexVersion)
c.co.setup(cmd)
c.svc = svc
c.out.setup(svc)
for _, prov := range storageProviders {
if prov.name == "from-config" {
continue
}
// Set up 'create' subcommand
f := prov.newFlags()
cc := cmd.Command(prov.name, "Create repository in "+prov.description)
f.setup(svc, cc)
cc.Action(func(_ *kingpin.ParseContext) error {
ctx := svc.rootContext()
st, err := f.connect(ctx, true)
if err != nil {
return errors.Wrap(err, "can't connect to storage")
}
return c.runCreateCommandWithStorage(ctx, st)
})
}
}
func (c *commandRepositoryCreate) newRepositoryOptionsFromFlags() *repo.NewRepositoryOptions {
return &repo.NewRepositoryOptions{
BlockFormat: content.FormattingOptions{
Hash: c.createBlockHashFormat,
Encryption: c.createBlockEncryptionFormat,
IndexVersion: c.createIndexVersion,
},
ObjectFormat: object.Format{
Splitter: c.createSplitter,
},
}
}
func (c *commandRepositoryCreate) ensureEmpty(ctx context.Context, s blob.Storage) error {
hasDataError := errors.Errorf("has data")
err := s.ListBlobs(ctx, "", func(cb blob.Metadata) error {
return hasDataError
})
if errors.Is(err, hasDataError) {
return errors.New("found existing data in storage location")
}
return errors.Wrap(err, "error listing blobs")
}
func (c *commandRepositoryCreate) runCreateCommandWithStorage(ctx context.Context, st blob.Storage) error {
err := c.ensureEmpty(ctx, st)
if err != nil {
return errors.Wrap(err, "unable to get repository storage")
}
options := c.newRepositoryOptionsFromFlags()
pass, err := c.svc.getPasswordFromFlags(ctx, true, false)
if err != nil {
return errors.Wrap(err, "getting password")
}
log(ctx).Infof("Initializing repository with:")
log(ctx).Infof(" block hash: %v", options.BlockFormat.Hash)
log(ctx).Infof(" encryption: %v", options.BlockFormat.Encryption)
log(ctx).Infof(" splitter: %v", options.ObjectFormat.Splitter)
if err := repo.Initialize(ctx, st, options, pass); err != nil {
return errors.Wrap(err, "cannot initialize repository")
}
if c.createOnly {
return nil
}
if err := c.svc.runConnectCommandWithStorageAndPassword(ctx, &c.co, st, pass); err != nil {
return errors.Wrap(err, "unable to connect to repository")
}
return c.populateRepository(ctx, pass)
}
func (c *commandRepositoryCreate) populateRepository(ctx context.Context, password string) error {
rep, err := repo.Open(ctx, c.svc.repositoryConfigFileName(), password, c.svc.optionsFromFlags(ctx))
if err != nil {
return errors.Wrap(err, "unable to open repository")
}
defer rep.Close(ctx) //nolint:errcheck
// nolint:wrapcheck
return repo.WriteSession(ctx, rep, repo.WriteSessionOptions{
Purpose: "populate repository",
}, func(ctx context.Context, w repo.RepositoryWriter) error {
if err := policy.SetPolicy(ctx, w, policy.GlobalPolicySourceInfo, policy.DefaultPolicy); err != nil {
return errors.Wrap(err, "unable to set global policy")
}
printRetentionPolicy(&c.out, policy.DefaultPolicy, nil)
printCompressionPolicy(&c.out, policy.DefaultPolicy, nil)
c.out.printStderr("\nTo find more information about default policy run 'kopia policy get'.\nTo change the policy use 'kopia policy set' command.\n")
if err := setDefaultMaintenanceParameters(ctx, w); err != nil {
return errors.Wrap(err, "unable to set maintenance parameters")
}
return nil
})
}