feat(cli): show storage capacity in repo status (#1867)

The connected repository's backing storage capacity and available
space can be now retrieved from `kopia repository status`. In text
format, these fields are printed in a human friendly form (MiB, GiB).
In JSON mode (`--json`), they are output as bytes.

Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com>
Co-authored-by: Julio
This commit is contained in:
Ali Dowair
2022-04-01 04:03:51 +03:00
committed by GitHub
parent 08cf7eb936
commit 044792db30
3 changed files with 31 additions and 4 deletions

View File

@@ -34,6 +34,7 @@ type RepositoryStatus struct {
ClientOptions repo.ClientOptions `json:"clientOptions"`
Storage blob.ConnectionInfo `json:"storage"`
Capacity *blob.Capacity `json:"volume,omitempty"`
ContentFormat content.FormattingOptions `json:"contentFormat"`
ObjectFormat object.Format `json:"objectFormat"`
BlobRetention content.BlobCfgBlob `json:"blobRetention"`
@@ -50,7 +51,7 @@ func (c *commandRepositoryStatus) setup(svc advancedAppServices, parent commandP
c.jo.setup(svc, cmd)
}
func (c *commandRepositoryStatus) outputJSON(r repo.Repository) error {
func (c *commandRepositoryStatus) outputJSON(ctx context.Context, r repo.Repository) error {
s := RepositoryStatus{
ConfigFile: c.svc.repositoryConfigFileName(),
ClientOptions: r.ClientOptions(),
@@ -64,6 +65,15 @@ func (c *commandRepositoryStatus) outputJSON(r repo.Repository) error {
s.BlobRetention = dr.BlobCfg()
s.Storage = scrubber.ScrubSensitiveData(reflect.ValueOf(ci)).Interface().(blob.ConnectionInfo) // nolint:forcetypeassert
s.ContentFormat = scrubber.ScrubSensitiveData(reflect.ValueOf(dr.ContentReader().ContentFormat())).Interface().(content.FormattingOptions) // nolint:forcetypeassert
switch cp, err := dr.BlobVolume().GetCapacity(ctx); {
case err == nil:
s.Capacity = &cp
case errors.Is(err, blob.ErrNotAVolume):
// This is okay, we will just not populate the result.
default:
return errors.Wrap(err, "unable to get storage volume capacity")
}
}
c.out.printStdout("%s\n", c.jo.jsonBytes(s))
@@ -71,9 +81,10 @@ func (c *commandRepositoryStatus) outputJSON(r repo.Repository) error {
return nil
}
// nolint: funlen
func (c *commandRepositoryStatus) run(ctx context.Context, rep repo.Repository) error {
if c.jo.jsonOutput {
return c.outputJSON(rep)
return c.outputJSON(ctx, rep)
}
c.out.printStdout("Config file: %v\n", c.svc.repositoryConfigFileName())
@@ -99,6 +110,16 @@ func (c *commandRepositoryStatus) run(ctx context.Context, rep repo.Repository)
ci := dr.BlobReader().ConnectionInfo()
c.out.printStdout("Storage type: %v\n", ci.Type)
switch cp, err := dr.BlobVolume().GetCapacity(ctx); {
case err == nil:
c.out.printStdout("Storage capacity: %v\n", units.BytesStringBase10(int64(cp.SizeB)))
c.out.printStdout("Storage available: %v\n", units.BytesStringBase10(int64(cp.FreeB)))
case errors.Is(err, blob.ErrNotAVolume):
c.out.printStdout("Storage capacity: unbounded\n")
default:
return errors.Wrap(err, "unable to get storage volume capacity")
}
if cjson, err := json.MarshalIndent(scrubber.ScrubSensitiveData(reflect.ValueOf(ci.Config)).Interface(), " ", " "); err == nil {
c.out.printStdout("Storage config: %v\n", string(cjson))
}

View File

@@ -56,9 +56,9 @@ type OutputBuffer interface {
// Capacity describes the storage capacity and usage of a Volume.
type Capacity struct {
// Size of volume in bytes.
SizeB uint64
SizeB uint64 `json:"capacity,omitempty"`
// Available (writeable) space in bytes.
FreeB uint64
FreeB uint64 `json:"available"`
}
// Volume defines disk/volume access API to blob storage.

View File

@@ -50,6 +50,7 @@ type DirectRepository interface {
ObjectFormat() object.Format
BlobCfg() content.BlobCfgBlob
BlobReader() blob.Reader
BlobVolume() blob.Volume
ContentReader() content.Reader
IndexBlobs(ctx context.Context, includeInactive bool) ([]content.IndexBlobInfo, error)
Crypter() *content.Crypter
@@ -308,6 +309,11 @@ func (r *directRepository) BlobReader() blob.Reader {
return r.blobs
}
// BlobVolume returns the blob volume interface.
func (r *directRepository) BlobVolume() blob.Volume {
return r.blobs
}
// ContentReader returns the content reader.
func (r *directRepository) ContentReader() content.Reader {
return r.cmgr