github.com/rancher/longhorn-engine@v0.6.2/replica/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/golang/protobuf/ptypes/empty"
    10  	"golang.org/x/net/context"
    11  	"google.golang.org/grpc"
    12  
    13  	replicarpc "github.com/longhorn/longhorn-engine/replica/rpc"
    14  	syncagentrpc "github.com/longhorn/longhorn-engine/sync/rpc"
    15  	"github.com/longhorn/longhorn-engine/types"
    16  )
    17  
    18  const (
    19  	GRPCServiceCommonTimeout = 1 * time.Minute
    20  	GRPCServiceLongTimeout   = 24 * time.Hour
    21  )
    22  
    23  type ReplicaClient struct {
    24  	host                string
    25  	replicaServiceURL   string
    26  	syncAgentServiceURL string
    27  }
    28  
    29  func NewReplicaClient(address string) (*ReplicaClient, error) {
    30  	if strings.HasPrefix(address, "tcp://") {
    31  		address = strings.TrimPrefix(address, "tcp://")
    32  	}
    33  
    34  	if strings.HasPrefix(address, "http://") {
    35  		address = strings.TrimPrefix(address, "http://")
    36  	}
    37  
    38  	if strings.HasSuffix(address, "/v1") {
    39  		address = strings.TrimSuffix(address, "/v1")
    40  	}
    41  
    42  	parts := strings.Split(address, ":")
    43  	if len(parts) < 2 {
    44  		return nil, fmt.Errorf("Invalid address %s, must have a port in it", address)
    45  	}
    46  
    47  	port, err := strconv.Atoi(parts[1])
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	syncAgentServiceURL := strings.Replace(address, fmt.Sprintf(":%d", port), fmt.Sprintf(":%d", port+2), -1)
    53  
    54  	return &ReplicaClient{
    55  		host:                parts[0],
    56  		replicaServiceURL:   address,
    57  		syncAgentServiceURL: syncAgentServiceURL,
    58  	}, nil
    59  }
    60  
    61  func GetDiskInfo(info *replicarpc.DiskInfo) *types.DiskInfo {
    62  	diskInfo := &types.DiskInfo{
    63  		Name:        info.Name,
    64  		Parent:      info.Parent,
    65  		Children:    info.Children,
    66  		Removed:     info.Removed,
    67  		UserCreated: info.UserCreated,
    68  		Created:     info.Created,
    69  		Size:        info.Size,
    70  		Labels:      info.Labels,
    71  	}
    72  
    73  	if diskInfo.Labels == nil {
    74  		diskInfo.Labels = map[string]string{}
    75  	}
    76  
    77  	return diskInfo
    78  }
    79  
    80  func GetReplicaInfo(r *replicarpc.Replica) *types.ReplicaInfo {
    81  	replicaInfo := &types.ReplicaInfo{
    82  		Dirty:           r.Dirty,
    83  		Rebuilding:      r.Rebuilding,
    84  		Head:            r.Head,
    85  		Parent:          r.Parent,
    86  		Size:            r.Size,
    87  		SectorSize:      r.SectorSize,
    88  		BackingFile:     r.BackingFile,
    89  		State:           r.State,
    90  		Chain:           r.Chain,
    91  		Disks:           map[string]types.DiskInfo{},
    92  		RemainSnapshots: int(r.RemainSnapshots),
    93  		RevisionCounter: r.RevisionCounter,
    94  	}
    95  
    96  	for diskName, diskInfo := range r.Disks {
    97  		replicaInfo.Disks[diskName] = *GetDiskInfo(diskInfo)
    98  	}
    99  
   100  	return replicaInfo
   101  }
   102  
   103  func (c *ReplicaClient) GetReplica() (*types.ReplicaInfo, error) {
   104  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   105  	if err != nil {
   106  		return nil, fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   107  	}
   108  	defer conn.Close()
   109  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   110  
   111  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   112  	defer cancel()
   113  
   114  	replica, err := replicaServiceClient.ReplicaGet(ctx, &empty.Empty{})
   115  	if err != nil {
   116  		return nil, fmt.Errorf("failed to get replica %v: %v", c.replicaServiceURL, err)
   117  	}
   118  
   119  	return GetReplicaInfo(replica), nil
   120  }
   121  
   122  func (c *ReplicaClient) OpenReplica() error {
   123  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   124  	if err != nil {
   125  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   126  	}
   127  	defer conn.Close()
   128  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   129  
   130  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   131  	defer cancel()
   132  
   133  	if _, err := replicaServiceClient.ReplicaOpen(ctx, &empty.Empty{}); err != nil {
   134  		return fmt.Errorf("failed to open replica %v: %v", c.replicaServiceURL, err)
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  func (c *ReplicaClient) Close() error {
   141  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   142  	if err != nil {
   143  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   144  	}
   145  	defer conn.Close()
   146  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   147  
   148  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   149  	defer cancel()
   150  
   151  	if _, err := replicaServiceClient.ReplicaClose(ctx, &empty.Empty{}); err != nil {
   152  		return fmt.Errorf("failed to close replica %v: %v", c.replicaServiceURL, err)
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  func (c *ReplicaClient) ReloadReplica() (*types.ReplicaInfo, error) {
   159  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   160  	if err != nil {
   161  		return nil, fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   162  	}
   163  	defer conn.Close()
   164  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   165  
   166  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   167  	defer cancel()
   168  
   169  	replica, err := replicaServiceClient.ReplicaReload(ctx, &empty.Empty{})
   170  	if err != nil {
   171  		return nil, fmt.Errorf("failed to reload replica %v: %v", c.replicaServiceURL, err)
   172  	}
   173  
   174  	return GetReplicaInfo(replica), nil
   175  }
   176  
   177  func (c *ReplicaClient) Revert(name, created string) error {
   178  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   179  	if err != nil {
   180  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   181  	}
   182  	defer conn.Close()
   183  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   184  
   185  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   186  	defer cancel()
   187  
   188  	if _, err := replicaServiceClient.ReplicaRevert(ctx, &replicarpc.ReplicaRevertRequest{
   189  		Name:    name,
   190  		Created: created,
   191  	}); err != nil {
   192  		return fmt.Errorf("failed to revert replica %v: %v", c.replicaServiceURL, err)
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  func (c *ReplicaClient) RemoveDisk(disk string, force bool) error {
   199  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   200  	if err != nil {
   201  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   202  	}
   203  	defer conn.Close()
   204  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   205  
   206  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   207  	defer cancel()
   208  
   209  	if _, err := replicaServiceClient.DiskRemove(ctx, &replicarpc.DiskRemoveRequest{
   210  		Name:  disk,
   211  		Force: force,
   212  	}); err != nil {
   213  		return fmt.Errorf("failed to remove disk %v for replica %v: %v", disk, c.replicaServiceURL, err)
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  func (c *ReplicaClient) ReplaceDisk(target, source string) error {
   220  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   221  	if err != nil {
   222  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   223  	}
   224  	defer conn.Close()
   225  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   226  
   227  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   228  	defer cancel()
   229  
   230  	if _, err := replicaServiceClient.DiskReplace(ctx, &replicarpc.DiskReplaceRequest{
   231  		Target: target,
   232  		Source: source,
   233  	}); err != nil {
   234  		return fmt.Errorf("failed to replace disk %v with %v for replica %v: %v", target, source, c.replicaServiceURL, err)
   235  	}
   236  
   237  	return nil
   238  }
   239  
   240  func (c *ReplicaClient) PrepareRemoveDisk(disk string) ([]*types.PrepareRemoveAction, error) {
   241  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   242  	if err != nil {
   243  		return nil, fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   244  	}
   245  	defer conn.Close()
   246  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   247  
   248  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   249  	defer cancel()
   250  
   251  	reply, err := replicaServiceClient.DiskPrepareRemove(ctx, &replicarpc.DiskPrepareRemoveRequest{
   252  		Name: disk,
   253  	})
   254  
   255  	if err != nil {
   256  		return nil, fmt.Errorf("failed to prepare removing disk %v for replica %v: %v", disk, c.replicaServiceURL, err)
   257  	}
   258  
   259  	operations := []*types.PrepareRemoveAction{}
   260  	for _, op := range reply.Operations {
   261  		operations = append(operations, &types.PrepareRemoveAction{
   262  			Action: op.Action,
   263  			Source: op.Source,
   264  			Target: op.Target,
   265  		})
   266  	}
   267  
   268  	return operations, nil
   269  }
   270  
   271  func (c *ReplicaClient) MarkDiskAsRemoved(disk string) error {
   272  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   273  	if err != nil {
   274  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   275  	}
   276  	defer conn.Close()
   277  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   278  
   279  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   280  	defer cancel()
   281  
   282  	if _, err := replicaServiceClient.DiskMarkAsRemoved(ctx, &replicarpc.DiskMarkAsRemovedRequest{
   283  		Name: disk,
   284  	}); err != nil {
   285  		return fmt.Errorf("failed to mark disk %v as removed for replica %v: %v", disk, c.replicaServiceURL, err)
   286  	}
   287  
   288  	return nil
   289  }
   290  
   291  func (c *ReplicaClient) SetRebuilding(rebuilding bool) error {
   292  	conn, err := grpc.Dial(c.replicaServiceURL, grpc.WithInsecure())
   293  	if err != nil {
   294  		return fmt.Errorf("cannot connect to ReplicaService %v: %v", c.replicaServiceURL, err)
   295  	}
   296  	defer conn.Close()
   297  	replicaServiceClient := replicarpc.NewReplicaServiceClient(conn)
   298  
   299  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   300  	defer cancel()
   301  
   302  	if _, err := replicaServiceClient.RebuildingSet(ctx, &replicarpc.RebuildingSetRequest{
   303  		Rebuilding: rebuilding,
   304  	}); err != nil {
   305  		return fmt.Errorf("failed to set rebuilding to %v for replica %v: %v", rebuilding, c.replicaServiceURL, err)
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  func (c *ReplicaClient) RemoveFile(file string) error {
   312  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   313  	if err != nil {
   314  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   315  	}
   316  	defer conn.Close()
   317  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   318  
   319  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   320  	defer cancel()
   321  
   322  	if _, err := syncAgentServiceClient.FileRemove(ctx, &syncagentrpc.FileRemoveRequest{
   323  		FileName: file,
   324  	}); err != nil {
   325  		return fmt.Errorf("failed to remove file %v: %v", file, err)
   326  	}
   327  
   328  	return nil
   329  }
   330  
   331  func (c *ReplicaClient) RenameFile(oldFileName, newFileName string) error {
   332  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   333  	if err != nil {
   334  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   335  	}
   336  	defer conn.Close()
   337  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   338  
   339  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   340  	defer cancel()
   341  
   342  	if _, err := syncAgentServiceClient.FileRename(ctx, &syncagentrpc.FileRenameRequest{
   343  		OldFileName: oldFileName,
   344  		NewFileName: newFileName,
   345  	}); err != nil {
   346  		return fmt.Errorf("failed to rename or replace old file %v with new file %v: %v", oldFileName, newFileName, err)
   347  	}
   348  
   349  	return nil
   350  }
   351  
   352  func (c *ReplicaClient) SendFile(from, host string, port int32) error {
   353  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   354  	if err != nil {
   355  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   356  	}
   357  	defer conn.Close()
   358  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   359  
   360  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceLongTimeout)
   361  	defer cancel()
   362  
   363  	if _, err := syncAgentServiceClient.FileSend(ctx, &syncagentrpc.FileSendRequest{
   364  		FromFileName: from,
   365  		Host:         host,
   366  		Port:         port,
   367  	}); err != nil {
   368  		return fmt.Errorf("failed to send file %v to %v:%v: %v", from, host, port, err)
   369  	}
   370  
   371  	return nil
   372  }
   373  
   374  func (c *ReplicaClient) LaunchReceiver(toFilePath string) (string, int32, error) {
   375  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   376  	if err != nil {
   377  		return "", 0, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   378  	}
   379  	defer conn.Close()
   380  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   381  
   382  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   383  	defer cancel()
   384  
   385  	reply, err := syncAgentServiceClient.ReceiverLaunch(ctx, &syncagentrpc.ReceiverLaunchRequest{
   386  		ToFileName: toFilePath,
   387  	})
   388  	if err != nil {
   389  		return "", 0, fmt.Errorf("failed to launch receiver for %v: %v", toFilePath, err)
   390  	}
   391  
   392  	return c.host, reply.Port, nil
   393  }
   394  
   395  func (c *ReplicaClient) CreateBackup(snapshot, dest, volume string, labels []string, credential map[string]string) (*syncagentrpc.BackupCreateReply, error) {
   396  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   397  	if err != nil {
   398  		return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   399  	}
   400  	defer conn.Close()
   401  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   402  
   403  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   404  	defer cancel()
   405  
   406  	reply, err := syncAgentServiceClient.BackupCreate(ctx, &syncagentrpc.BackupCreateRequest{
   407  		SnapshotFileName: snapshot,
   408  		BackupTarget:     dest,
   409  		VolumeName:       volume,
   410  		Labels:           labels,
   411  		Credential:       credential,
   412  	})
   413  	if err != nil {
   414  		return nil, fmt.Errorf("failed to create backup to %v for volume %v: %v", dest, volume, err)
   415  	}
   416  
   417  	return reply, nil
   418  }
   419  
   420  func (c *ReplicaClient) GetBackupStatus(backupName string) (*syncagentrpc.BackupStatusReply, error) {
   421  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   422  	if err != nil {
   423  		return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   424  	}
   425  	defer conn.Close()
   426  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   427  
   428  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   429  	defer cancel()
   430  
   431  	reply, err := syncAgentServiceClient.BackupGetStatus(ctx, &syncagentrpc.BackupProgressRequest{
   432  		Backup: backupName,
   433  	})
   434  
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  
   439  	return reply, nil
   440  }
   441  
   442  func (c *ReplicaClient) RmBackup(backup string) error {
   443  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   444  	if err != nil {
   445  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   446  	}
   447  	defer conn.Close()
   448  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   449  
   450  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   451  	defer cancel()
   452  
   453  	if _, err := syncAgentServiceClient.BackupRemove(ctx, &syncagentrpc.BackupRemoveRequest{
   454  		Backup: backup,
   455  	}); err != nil {
   456  		return fmt.Errorf("failed to remove backup %v: %v", backup, err)
   457  	}
   458  
   459  	return nil
   460  }
   461  
   462  func (c *ReplicaClient) RestoreBackup(backup, snapshotFile string, credential map[string]string) error {
   463  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   464  	if err != nil {
   465  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   466  	}
   467  	defer conn.Close()
   468  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   469  
   470  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   471  	defer cancel()
   472  
   473  	if _, err := syncAgentServiceClient.BackupRestore(ctx, &syncagentrpc.BackupRestoreRequest{
   474  		Backup:           backup,
   475  		SnapshotFileName: snapshotFile,
   476  		Credential:       credential,
   477  	}); err != nil {
   478  		return fmt.Errorf("failed to restore backup %v to snapshot %v: %v", backup, snapshotFile, err)
   479  	}
   480  
   481  	return nil
   482  }
   483  
   484  func (c *ReplicaClient) RestoreBackupIncrementally(backup, deltaFile, lastRestored,
   485  	snapshotDiskName string, credential map[string]string) error {
   486  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   487  	if err != nil {
   488  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   489  	}
   490  	defer conn.Close()
   491  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   492  
   493  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   494  	defer cancel()
   495  
   496  	if _, err := syncAgentServiceClient.BackupRestoreIncrementally(ctx, &syncagentrpc.BackupRestoreIncrementallyRequest{
   497  		Backup:                 backup,
   498  		DeltaFileName:          deltaFile,
   499  		LastRestoredBackupName: lastRestored,
   500  		Credential:             credential,
   501  		SnapshotDiskName:       snapshotDiskName,
   502  	}); err != nil {
   503  		return fmt.Errorf("failed to incrementally restore backup %v to file %v: %v", backup, deltaFile, err)
   504  	}
   505  
   506  	return nil
   507  }
   508  func (c *ReplicaClient) Reset() error {
   509  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   510  	if err != nil {
   511  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   512  	}
   513  	defer conn.Close()
   514  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   515  
   516  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   517  	defer cancel()
   518  
   519  	if _, err := syncAgentServiceClient.Reset(ctx, &empty.Empty{}); err != nil {
   520  		return fmt.Errorf("failed to cleanup restore info in Sync Agent Server: %v", err)
   521  	}
   522  
   523  	return nil
   524  }
   525  
   526  func (c *ReplicaClient) RestoreStatus() (*syncagentrpc.RestoreStatusReply, error) {
   527  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   528  	if err != nil {
   529  		return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   530  	}
   531  	defer conn.Close()
   532  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   533  
   534  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   535  	defer cancel()
   536  
   537  	reply, err := syncAgentServiceClient.RestoreStatus(ctx, &empty.Empty{})
   538  	if err != nil {
   539  		return nil, fmt.Errorf("failed to get restore status: %v", err)
   540  	}
   541  
   542  	return reply, nil
   543  }
   544  
   545  func (c *ReplicaClient) SnapshotPurge() error {
   546  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   547  	if err != nil {
   548  		return fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   549  	}
   550  	defer conn.Close()
   551  
   552  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   553  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   554  	defer cancel()
   555  
   556  	if _, err := syncAgentServiceClient.SnapshotPurge(ctx, &empty.Empty{}); err != nil {
   557  		return fmt.Errorf("failed to start snapshot purge: %v", err)
   558  	}
   559  
   560  	return nil
   561  }
   562  
   563  func (c *ReplicaClient) SnapshotPurgeStatus() (*syncagentrpc.SnapshotPurgeStatusReply, error) {
   564  	conn, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithInsecure())
   565  	if err != nil {
   566  		return nil, fmt.Errorf("cannot connect to SyncAgentService %v: %v", c.syncAgentServiceURL, err)
   567  	}
   568  	defer conn.Close()
   569  
   570  	syncAgentServiceClient := syncagentrpc.NewSyncAgentServiceClient(conn)
   571  	ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceCommonTimeout)
   572  	defer cancel()
   573  
   574  	status, err := syncAgentServiceClient.SnapshotPurgeStatus(ctx, &empty.Empty{})
   575  	if err != nil {
   576  		return nil, fmt.Errorf("failed to get snapshot purge status: %v", err)
   577  	}
   578  
   579  	return status, nil
   580  }