mirror of
https://github.com/kopia/kopia.git
synced 2026-01-25 14:58:00 -05:00
- `repo.Repository` is now read-only and only has methods that can be supported over kopia server
- `repo.RepositoryWriter` has read-write methods that can be supported over kopia server
- `repo.DirectRepository` is read-only and contains all methods of `repo.Repository` plus some low-level methods for data inspection
- `repo.DirectRepositoryWriter` contains write methods for `repo.DirectRepository`
- `repo.Reader` removed and merged with `repo.Repository`
- `repo.Writer` became `repo.RepositoryWriter`
- `*repo.DirectRepository` struct became `repo.DirectRepository`
interface
Getting `{Direct}RepositoryWriter` requires using `NewWriter()` or `NewDirectWriter()` on a read-only repository and multiple simultaneous writers are supported at the same time, each writing to their own indexes and pack blobs.
`repo.Open` returns `repo.Repository` (which is also `repo.RepositoryWriter`).
* content: removed implicit flush on content manager close
* repo: added tests for WriteSession() and implicit flush behavior
* invalidate manifest manager after write session
* cli: disable maintenance in 'kopia server start'
Server will close the repository before completing.
* repo: unconditionally close RepositoryWriter in {Direct,}WriteSession
* repo: added panic in case somebody tries to create RepositoryWriter after closing repository
- used atomic to manage SharedManager.closed
* removed stale example
* linter: fixed spurious failures
Co-authored-by: Julio López <julio+gh@kasten.io>
115 lines
3.1 KiB
Go
115 lines
3.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"reflect"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/internal/scrubber"
|
|
"github.com/kopia/kopia/internal/units"
|
|
"github.com/kopia/kopia/repo"
|
|
)
|
|
|
|
var (
|
|
statusCommand = repositoryCommands.Command("status", "Display the status of connected repository.")
|
|
statusReconnectToken = statusCommand.Flag("reconnect-token", "Display reconnect command").Short('t').Bool()
|
|
statusReconnectTokenIncludePassword = statusCommand.Flag("reconnect-token-with-password", "Include password in reconnect token").Short('s').Bool()
|
|
)
|
|
|
|
func runStatusCommand(ctx context.Context, rep repo.Repository) error {
|
|
fmt.Printf("Config file: %v\n", repositoryConfigFileName())
|
|
fmt.Println()
|
|
fmt.Printf("Description: %v\n", rep.ClientOptions().Description)
|
|
fmt.Printf("Hostname: %v\n", rep.ClientOptions().Hostname)
|
|
fmt.Printf("Username: %v\n", rep.ClientOptions().Username)
|
|
fmt.Printf("Read-only: %v\n", rep.ClientOptions().ReadOnly)
|
|
|
|
dr, ok := rep.(repo.DirectRepository)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
ci := dr.BlobReader().ConnectionInfo()
|
|
fmt.Printf("Storage type: %v\n", ci.Type)
|
|
|
|
if cjson, err := json.MarshalIndent(scrubber.ScrubSensitiveData(reflect.ValueOf(ci.Config)).Interface(), " ", " "); err == nil {
|
|
fmt.Printf("Storage config: %v\n", string(cjson))
|
|
}
|
|
|
|
fmt.Println()
|
|
fmt.Printf("Unique ID: %x\n", dr.UniqueID())
|
|
fmt.Printf("Hash: %v\n", dr.ContentReader().ContentFormat().Hash)
|
|
fmt.Printf("Encryption: %v\n", dr.ContentReader().ContentFormat().Encryption)
|
|
fmt.Printf("Splitter: %v\n", dr.ObjectFormat().Splitter)
|
|
fmt.Printf("Format version: %v\n", dr.ContentReader().ContentFormat().Version)
|
|
fmt.Printf("Max pack length: %v\n", units.BytesStringBase2(int64(dr.ContentReader().ContentFormat().MaxPackSize)))
|
|
|
|
if !*statusReconnectToken {
|
|
return nil
|
|
}
|
|
|
|
pass := ""
|
|
|
|
if *statusReconnectTokenIncludePassword {
|
|
var err error
|
|
|
|
pass, err = getPasswordFromFlags(ctx, false, true)
|
|
if err != nil {
|
|
return errors.Wrap(err, "getting password")
|
|
}
|
|
}
|
|
|
|
tok, err := dr.Token(pass)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error computing repository token")
|
|
}
|
|
|
|
fmt.Printf("\nTo reconnect to the repository use:\n\n$ kopia repository connect from-config --token %v\n\n", tok)
|
|
|
|
if pass != "" {
|
|
fmt.Printf("NOTICE: The token printed above can be trivially decoded to reveal the repository password. Do not store it in an unsecured place.\n")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func scanCacheDir(dirname string) (fileCount int, totalFileLength int64, err error) {
|
|
entries, err := ioutil.ReadDir(dirname)
|
|
if err != nil {
|
|
return 0, 0, nil
|
|
}
|
|
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
subdir := filepath.Join(dirname, e.Name())
|
|
|
|
c, l, err2 := scanCacheDir(subdir)
|
|
if err2 != nil {
|
|
return 0, 0, err2
|
|
}
|
|
|
|
fileCount += c
|
|
totalFileLength += l
|
|
|
|
continue
|
|
}
|
|
|
|
fileCount++
|
|
|
|
totalFileLength += e.Size()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func init() {
|
|
statusCommand.Action(repositoryReaderAction(runStatusCommand))
|
|
}
|