github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/client/cmd_simplefs_sync_show.go (about)

     1  // Copyright 2018 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package client
     5  
     6  import (
     7  	"fmt"
     8  	"path"
     9  	"time"
    10  
    11  	"github.com/keybase/cli"
    12  	"github.com/keybase/client/go/libcmdline"
    13  	"github.com/keybase/client/go/libkb"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	"golang.org/x/net/context"
    16  )
    17  
    18  // CmdSimpleFSSyncShow is the 'fs sync show' command.
    19  type CmdSimpleFSSyncShow struct {
    20  	libkb.Contextified
    21  	path   keybase1.Path
    22  	getAll bool
    23  }
    24  
    25  // NewCmdSimpleFSSyncShow creates a new cli.Command.
    26  func NewCmdSimpleFSSyncShow(
    27  	cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
    28  	return cli.Command{
    29  		Name:         "show",
    30  		ArgumentHelp: "<path-to-folder>",
    31  		Usage:        "shows the sync configuration and status for the given folder, or all folders if none is specified",
    32  		Action: func(c *cli.Context) {
    33  			cl.ChooseCommand(&CmdSimpleFSSyncShow{
    34  				Contextified: libkb.NewContextified(g)}, "show", c)
    35  			cl.SetNoStandalone()
    36  		},
    37  	}
    38  }
    39  
    40  func printBytesStored(ui libkb.TerminalUI, bytes int64, tab string) {
    41  	if bytes >= 0 {
    42  		ui.Printf("%s%s bytes stored\n", tab, humanizeBytes(bytes, false))
    43  	}
    44  }
    45  
    46  func printPrefetchStatus(
    47  	ui libkb.TerminalUI, status keybase1.FolderSyncStatus, tab string) {
    48  	switch status.PrefetchStatus {
    49  	case keybase1.PrefetchStatus_COMPLETE:
    50  		ui.Printf("%sStatus: fully synced\n", tab)
    51  	case keybase1.PrefetchStatus_IN_PROGRESS:
    52  		ui.Printf("%sStatus: sync in progress\n", tab)
    53  		progress := status.PrefetchProgress
    54  		if progress.BytesTotal > 0 {
    55  			ui.Printf("%s%s/%s (%.2f%%)\n",
    56  				tab, humanizeBytes(progress.BytesFetched, false),
    57  				humanizeBytes(progress.BytesTotal, false),
    58  				100*float64(progress.BytesFetched)/
    59  					float64(progress.BytesTotal))
    60  		}
    61  		if progress.EndEstimate > 0 && !status.OutOfSyncSpace {
    62  			timeRemaining := time.Until(keybase1.FromTime(progress.EndEstimate))
    63  			ui.Printf("%sEstimated time remaining: %s\n",
    64  				tab, timeRemaining.Round(1*time.Second))
    65  		} else if status.OutOfSyncSpace {
    66  			ui.Printf("%sError: out of disk space\n", tab)
    67  		}
    68  	case keybase1.PrefetchStatus_NOT_STARTED:
    69  		ui.Printf("%sStatus: sync not yet started\n", tab)
    70  	default:
    71  		ui.Printf("%sStatus: unknown\n", tab)
    72  	}
    73  	printBytesStored(ui, status.StoredBytesTotal, tab)
    74  }
    75  
    76  func appendToTlfPath(tlfPath keybase1.Path, p string) (keybase1.Path, error) {
    77  	return makeSimpleFSPath(
    78  		path.Join([]string{mountDir, tlfPath.String(), p}...))
    79  }
    80  
    81  func printLocalStats(
    82  	ui libkb.TerminalUI, status keybase1.FolderSyncStatus) {
    83  	a := status.LocalDiskBytesAvailable
    84  	t := status.LocalDiskBytesTotal
    85  	ui.Printf("%s (%.2f%%) of the local disk is available for caching.\n",
    86  		humanizeBytes(a, false), float64(a)/float64(t)*100)
    87  }
    88  
    89  func printFolderStatus(
    90  	ctx context.Context, cli keybase1.SimpleFSClient, ui libkb.TerminalUI,
    91  	config keybase1.FolderSyncConfig, status keybase1.FolderSyncStatus,
    92  	tab string, tlfPath keybase1.Path, doPrintLocalStats bool) error {
    93  	switch config.Mode {
    94  	case keybase1.FolderSyncMode_DISABLED:
    95  		ui.Printf("%sSyncing disabled\n", tab)
    96  	case keybase1.FolderSyncMode_ENABLED:
    97  		ui.Printf("%sSyncing enabled\n", tab)
    98  		printPrefetchStatus(ui, status, tab)
    99  		if doPrintLocalStats {
   100  			printLocalStats(ui, status)
   101  		}
   102  	case keybase1.FolderSyncMode_PARTIAL:
   103  		// Show all the paths for the TLF, even if a more specific
   104  		// path was passed in.
   105  		paths := "these subpaths"
   106  		if len(config.Paths) == 1 {
   107  			paths = "this subpath"
   108  		}
   109  		ui.Printf("%sSyncing configured for %s:\n", tab, paths)
   110  		for _, p := range config.Paths {
   111  			fullPath, err := appendToTlfPath(tlfPath, p)
   112  			if err != nil {
   113  				ui.Printf("\tError: %v", err)
   114  				continue
   115  			}
   116  			e, err := cli.SimpleFSStat(
   117  				ctx, keybase1.SimpleFSStatArg{Path: fullPath})
   118  			if err != nil {
   119  				ui.Printf("\tError: %v", err)
   120  				continue
   121  			}
   122  
   123  			ui.Printf("%s\t%s\n", tab, p)
   124  			pathStatus := keybase1.FolderSyncStatus{
   125  				PrefetchStatus:   e.PrefetchStatus,
   126  				PrefetchProgress: e.PrefetchProgress,
   127  				StoredBytesTotal: -1,
   128  				OutOfSyncSpace:   status.OutOfSyncSpace,
   129  			}
   130  			printPrefetchStatus(ui, pathStatus, tab+"\t\t")
   131  		}
   132  		printBytesStored(ui, status.StoredBytesTotal, tab)
   133  		if doPrintLocalStats {
   134  			printLocalStats(ui, status)
   135  		}
   136  	default:
   137  		return fmt.Errorf("Unknown sync mode: %s", config.Mode)
   138  	}
   139  	return nil
   140  }
   141  
   142  // Run runs the command in client/server mode.
   143  func (c *CmdSimpleFSSyncShow) Run() error {
   144  	cli, err := GetSimpleFSClient(c.G())
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	ctx := context.TODO()
   150  	ui := c.G().UI.GetTerminalUI()
   151  	if c.getAll {
   152  		res, err := cli.SimpleFSSyncConfigAndStatus(ctx, nil)
   153  		if err != nil {
   154  			return err
   155  		}
   156  
   157  		for _, folder := range res.Folders {
   158  			p, err := makeSimpleFSPath(mountDir + "/" + folder.Folder.String())
   159  			if err != nil {
   160  				return err
   161  			}
   162  			ui.Printf("%s\n", folder.Folder)
   163  			err = printFolderStatus(
   164  				ctx, cli, ui, folder.Config, folder.Status, "  ", p, false)
   165  			if err != nil {
   166  				return err
   167  			}
   168  			ui.Printf("\n")
   169  		}
   170  
   171  		printPrefetchStatus(ui, res.OverallStatus, "")
   172  		printLocalStats(ui, res.OverallStatus)
   173  	} else {
   174  		res, err := cli.SimpleFSFolderSyncConfigAndStatus(ctx, c.path)
   175  		if err != nil {
   176  			return err
   177  		}
   178  		tlfPath, err := toTlfPath(c.path)
   179  		if err != nil {
   180  			return err
   181  		}
   182  		return printFolderStatus(
   183  			ctx, cli, ui, res.Config, res.Status, "", tlfPath, true)
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // ParseArgv gets the required path.
   190  func (c *CmdSimpleFSSyncShow) ParseArgv(ctx *cli.Context) error {
   191  	if len(ctx.Args()) > 1 {
   192  		return fmt.Errorf("wrong number of arguments")
   193  	}
   194  
   195  	if len(ctx.Args()) == 1 {
   196  		p, err := makeSimpleFSPath(ctx.Args()[0])
   197  		if err != nil {
   198  			return err
   199  		}
   200  		c.path = p
   201  	} else {
   202  		c.getAll = true
   203  	}
   204  	return nil
   205  }
   206  
   207  // GetUsage says what this command needs to operate.
   208  func (c *CmdSimpleFSSyncShow) GetUsage() libkb.Usage {
   209  	return libkb.Usage{
   210  		Config:    true,
   211  		KbKeyring: true,
   212  		API:       true,
   213  	}
   214  }