github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/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  type APIClient interface {
    25  	io.Closer
    26  	// Create sends an RPC request to create a new backup.
    27  	Create(notes string) (*params.BackupsMetadataResult, error)
    28  	// Info gets the backup's metadata.
    29  	Info(id string) (*params.BackupsMetadataResult, error)
    30  	// List gets all stored metadata.
    31  	List() (*params.BackupsListResult, error)
    32  	// Download pulls the backup archive file.
    33  	Download(id string) (io.ReadCloser, error)
    34  	// Upload pushes a backup archive to storage.
    35  	Upload(ar io.ReadSeeker, meta params.BackupsMetadataResult) (string, error)
    36  	// Remove removes the stored backup.
    37  	Remove(id string) error
    38  	// Restore will restore a backup with the given id into the controller.
    39  	Restore(string, backups.ClientConnection) error
    40  	// RestoreReader will restore a backup file into the controller.
    41  	RestoreReader(io.ReadSeeker, *params.BackupsMetadataResult, backups.ClientConnection) error
    42  }
    43  
    44  // CommandBase is the base type for backups sub-commands.
    45  type CommandBase struct {
    46  	// TODO(wallyworld) - remove Log when backup command is flattened.
    47  	Log *cmd.Log
    48  	modelcmd.ModelCommandBase
    49  }
    50  
    51  // NewAPIClient returns a client for the backups api endpoint.
    52  func (c *CommandBase) NewAPIClient() (APIClient, error) {
    53  	return newAPIClient(c)
    54  }
    55  
    56  // SetFlags implements Command.SetFlags.
    57  func (c *CommandBase) SetFlags(f *gnuflag.FlagSet) {
    58  	c.ModelCommandBase.SetFlags(f)
    59  	if c.Log != nil {
    60  		c.Log.AddFlags(f)
    61  	}
    62  }
    63  
    64  var newAPIClient = func(c *CommandBase) (APIClient, error) {
    65  	root, err := c.NewAPIRoot()
    66  	if err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	return backups.NewClient(root)
    70  }
    71  
    72  // dumpMetadata writes the formatted backup metadata to stdout.
    73  func (c *CommandBase) dumpMetadata(ctx *cmd.Context, result *params.BackupsMetadataResult) {
    74  	fmt.Fprintf(ctx.Stdout, "backup ID:       %q\n", result.ID)
    75  	fmt.Fprintf(ctx.Stdout, "checksum:        %q\n", result.Checksum)
    76  	fmt.Fprintf(ctx.Stdout, "checksum format: %q\n", result.ChecksumFormat)
    77  	fmt.Fprintf(ctx.Stdout, "size (B):        %d\n", result.Size)
    78  	fmt.Fprintf(ctx.Stdout, "stored:          %v\n", result.Stored)
    79  
    80  	fmt.Fprintf(ctx.Stdout, "started:         %v\n", result.Started)
    81  	fmt.Fprintf(ctx.Stdout, "finished:        %v\n", result.Finished)
    82  	fmt.Fprintf(ctx.Stdout, "notes:           %q\n", result.Notes)
    83  
    84  	fmt.Fprintf(ctx.Stdout, "model ID:        %q\n", result.Model)
    85  	fmt.Fprintf(ctx.Stdout, "machine ID:      %q\n", result.Machine)
    86  	fmt.Fprintf(ctx.Stdout, "created on host: %q\n", result.Hostname)
    87  	fmt.Fprintf(ctx.Stdout, "juju version:    %v\n", result.Version)
    88  }
    89  
    90  // ArchiveReader can read a backup archive.
    91  type ArchiveReader interface {
    92  	io.ReadSeeker
    93  	io.Closer
    94  }
    95  
    96  func getArchive(filename string) (rc ArchiveReader, metaResult *params.BackupsMetadataResult, err error) {
    97  	defer func() {
    98  		if err != nil && rc != nil {
    99  			rc.Close()
   100  		}
   101  	}()
   102  	archive, err := os.Open(filename)
   103  	rc = archive
   104  	if err != nil {
   105  		return nil, nil, errors.Trace(err)
   106  	}
   107  
   108  	// Extract the metadata.
   109  	ad, err := statebackups.NewArchiveDataReader(archive)
   110  	if err != nil {
   111  		return nil, nil, errors.Trace(err)
   112  	}
   113  	_, err = archive.Seek(0, os.SEEK_SET)
   114  	if err != nil {
   115  		return nil, nil, errors.Trace(err)
   116  	}
   117  	meta, err := ad.Metadata()
   118  	if err != nil {
   119  		if !errors.IsNotFound(err) {
   120  			return nil, nil, errors.Trace(err)
   121  		}
   122  		meta, err = statebackups.BuildMetadata(archive)
   123  		if err != nil {
   124  			return nil, nil, errors.Trace(err)
   125  		}
   126  	}
   127  	// Make sure the file info is set.
   128  	fileMeta, err := statebackups.BuildMetadata(archive)
   129  	if err != nil {
   130  		return nil, nil, errors.Trace(err)
   131  	}
   132  	if meta.Size() == int64(0) {
   133  		if err := meta.SetFileInfo(fileMeta.Size(), "", ""); err != nil {
   134  			return nil, nil, errors.Trace(err)
   135  		}
   136  	}
   137  	if meta.Checksum() == "" {
   138  		err := meta.SetFileInfo(0, fileMeta.Checksum(), fileMeta.ChecksumFormat())
   139  		if err != nil {
   140  			return nil, nil, errors.Trace(err)
   141  		}
   142  	}
   143  	if meta.Finished == nil || meta.Finished.IsZero() {
   144  		meta.Finished = fileMeta.Finished
   145  	}
   146  	_, err = archive.Seek(0, os.SEEK_SET)
   147  	if err != nil {
   148  		return nil, nil, errors.Trace(err)
   149  	}
   150  
   151  	// Pack the metadata into a result.
   152  	// TODO(perrito666) change the identity of ResultfromMetadata to
   153  	// return a pointer.
   154  	mResult := apiserverbackups.ResultFromMetadata(meta)
   155  	metaResult = &mResult
   156  
   157  	return archive, metaResult, nil
   158  }