github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/backups/backups.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package backups
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/gnuflag"
    14  
    15  	"github.com/juju/juju/api/backups"
    16  	apiserverbackups "github.com/juju/juju/apiserver/facades/client/backups"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/cmd/modelcmd"
    19  	statebackups "github.com/juju/juju/state/backups"
    20  )
    21  
    22  // APIClient represents the backups API client functionality used by
    23  // the backups command.
    24  //
    25  // To regenerate the mocks for the APIClient used by this package,
    26  // run "go generate" from the package directory.
    27  //go:generate mockgen -package backups_test -destination mock_test.go github.com/juju/juju/cmd/juju/backups ArchiveReader,APIClient
    28  type APIClient interface {
    29  	io.Closer
    30  	// Create sends an RPC request to create a new backup.
    31  	Create(notes string, keepCopy, noDownload bool) (*params.BackupsMetadataResult, error)
    32  	// Info gets the backup's metadata.
    33  	Info(id string) (*params.BackupsMetadataResult, error)
    34  	// List gets all stored metadata.
    35  	List() (*params.BackupsListResult, error)
    36  	// Download pulls the backup archive file.
    37  	Download(id string) (io.ReadCloser, error)
    38  	// Upload pushes a backup archive to storage.
    39  	Upload(ar io.ReadSeeker, meta params.BackupsMetadataResult) (string, error)
    40  	// Remove removes the stored backups.
    41  	Remove(ids ...string) ([]params.ErrorResult, error)
    42  	// Restore will restore a backup with the given id into the controller.
    43  	Restore(string, backups.ClientConnection) error
    44  	// RestoreReader will restore a backup file into the controller.
    45  	RestoreReader(io.ReadSeeker, *params.BackupsMetadataResult, backups.ClientConnection) error
    46  }
    47  
    48  // CommandBase is the base type for backups sub-commands.
    49  type CommandBase struct {
    50  	// TODO(wallyworld) - remove Log when backup command is flattened.
    51  	Log *cmd.Log
    52  	modelcmd.ModelCommandBase
    53  }
    54  
    55  // NewAPIClient returns a client for the backups api endpoint.
    56  func (c *CommandBase) NewAPIClient() (APIClient, error) {
    57  	return newAPIClient(c)
    58  }
    59  
    60  // NewAPIClient returns a client for the backups api endpoint.
    61  func (c *CommandBase) NewGetAPI() (APIClient, int, error) {
    62  	return getAPI(c)
    63  }
    64  
    65  // SetFlags implements Command.SetFlags.
    66  func (c *CommandBase) SetFlags(f *gnuflag.FlagSet) {
    67  	c.ModelCommandBase.SetFlags(f)
    68  	if c.Log != nil {
    69  		c.Log.AddFlags(f)
    70  	}
    71  }
    72  
    73  var newAPIClient = func(c *CommandBase) (APIClient, error) {
    74  	root, err := c.NewAPIRoot()
    75  	if err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  	return backups.NewClient(root)
    79  }
    80  
    81  // GetAPI returns a client and the api version of the controller
    82  var getAPI = func(c *CommandBase) (APIClient, int, error) {
    83  	root, err := c.NewAPIRoot()
    84  	if err != nil {
    85  		return nil, -1, errors.Trace(err)
    86  	}
    87  	version := root.BestFacadeVersion("Backups")
    88  	client, err := backups.NewClient(root)
    89  	return client, version, nil
    90  }
    91  
    92  // dumpMetadata writes the formatted backup metadata to stdout.
    93  func (c *CommandBase) dumpMetadata(ctx *cmd.Context, result *params.BackupsMetadataResult) {
    94  	// TODO: (hml) 2018-04-26
    95  	// fix how --quiet and --verbose are handled with backup/restore commands
    96  	// should be ctx.Verbosef() here
    97  	fmt.Fprintf(ctx.Stdout, "backup ID:       %q\n", result.ID)
    98  	fmt.Fprintf(ctx.Stdout, "checksum:        %q\n", result.Checksum)
    99  	fmt.Fprintf(ctx.Stdout, "checksum format: %q\n", result.ChecksumFormat)
   100  	fmt.Fprintf(ctx.Stdout, "size (B):        %d\n", result.Size)
   101  	fmt.Fprintf(ctx.Stdout, "stored:          %v\n", result.Stored)
   102  
   103  	fmt.Fprintf(ctx.Stdout, "started:         %v\n", result.Started)
   104  	fmt.Fprintf(ctx.Stdout, "finished:        %v\n", result.Finished)
   105  	fmt.Fprintf(ctx.Stdout, "notes:           %q\n", result.Notes)
   106  
   107  	fmt.Fprintf(ctx.Stdout, "model ID:        %q\n", result.Model)
   108  	fmt.Fprintf(ctx.Stdout, "machine ID:      %q\n", result.Machine)
   109  	fmt.Fprintf(ctx.Stdout, "created on host: %q\n", result.Hostname)
   110  	fmt.Fprintf(ctx.Stdout, "juju version:    %v\n", result.Version)
   111  }
   112  
   113  // ArchiveReader can read a backup archive.
   114  //
   115  // To regenerate the mocks for the ArchiveReader used by this package,
   116  // run "go generate" from the package directory.
   117  //go:generate mockgen -package backups_test -destination mock_test.go github.com/juju/juju/cmd/juju/backups ArchiveReader,APIClient
   118  type ArchiveReader interface {
   119  	io.ReadSeeker
   120  	io.Closer
   121  }
   122  
   123  var getArchive = func(filename string) (rc ArchiveReader, metaResult *params.BackupsMetadataResult, err error) {
   124  	defer func() {
   125  		if err != nil && rc != nil {
   126  			rc.Close()
   127  		}
   128  	}()
   129  	archive, err := os.Open(filename)
   130  	rc = archive
   131  	if err != nil {
   132  		return nil, nil, errors.Trace(err)
   133  	}
   134  
   135  	// Extract the metadata.
   136  	ad, err := statebackups.NewArchiveDataReader(archive)
   137  	if err != nil {
   138  		return nil, nil, errors.Trace(err)
   139  	}
   140  	_, err = archive.Seek(0, os.SEEK_SET)
   141  	if err != nil {
   142  		return nil, nil, errors.Trace(err)
   143  	}
   144  	meta, err := ad.Metadata()
   145  	if err != nil {
   146  		if !errors.IsNotFound(err) {
   147  			return nil, nil, errors.Trace(err)
   148  		}
   149  		meta, err = statebackups.BuildMetadata(archive)
   150  		if err != nil {
   151  			return nil, nil, errors.Trace(err)
   152  		}
   153  	}
   154  	// Make sure the file info is set.
   155  	fileMeta, err := statebackups.BuildMetadata(archive)
   156  	if err != nil {
   157  		return nil, nil, errors.Trace(err)
   158  	}
   159  	if meta.Size() == int64(0) {
   160  		if err := meta.SetFileInfo(fileMeta.Size(), "", ""); err != nil {
   161  			return nil, nil, errors.Trace(err)
   162  		}
   163  	}
   164  	if meta.Checksum() == "" {
   165  		err := meta.SetFileInfo(0, fileMeta.Checksum(), fileMeta.ChecksumFormat())
   166  		if err != nil {
   167  			return nil, nil, errors.Trace(err)
   168  		}
   169  	}
   170  	if meta.Finished == nil || meta.Finished.IsZero() {
   171  		meta.Finished = fileMeta.Finished
   172  	}
   173  	_, err = archive.Seek(0, os.SEEK_SET)
   174  	if err != nil {
   175  		return nil, nil, errors.Trace(err)
   176  	}
   177  
   178  	// Pack the metadata into a result.
   179  	// TODO(perrito666) change the identity of ResultfromMetadata to
   180  	// return a pointer.
   181  	mResult := apiserverbackups.CreateResult(meta, "")
   182  	metaResult = &mResult
   183  
   184  	return archive, metaResult, nil
   185  }