github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/backups/upload.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  	"launchpad.net/gnuflag"
    14  
    15  	apiserverbackups "github.com/juju/juju/apiserver/backups"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/state/backups"
    18  )
    19  
    20  const uploadDoc = `
    21  "upload" sends a backup archive file to remote storage.
    22  `
    23  
    24  // UploadCommand is the sub-command for uploading a backup archive.
    25  type UploadCommand struct {
    26  	CommandBase
    27  	// Filename is where to find the archive to upload.
    28  	Filename string
    29  	// ShowMeta indicates that the uploaded metadata should be printed.
    30  	ShowMeta bool
    31  	// Quiet indicates that the new backup ID should not be printed.
    32  	Quiet bool
    33  }
    34  
    35  // SetFlags implements Command.SetFlags.
    36  func (c *UploadCommand) SetFlags(f *gnuflag.FlagSet) {
    37  	f.BoolVar(&c.ShowMeta, "verbose", false, "show the uploaded metadata")
    38  	f.BoolVar(&c.Quiet, "quiet", false, "do not print the new backup ID")
    39  }
    40  
    41  // Info implements Command.Info.
    42  func (c *UploadCommand) Info() *cmd.Info {
    43  	return &cmd.Info{
    44  		Name:    "upload",
    45  		Args:    "<filename>",
    46  		Purpose: "store a backup archive file remotely in juju",
    47  		Doc:     uploadDoc,
    48  	}
    49  }
    50  
    51  // Init implements Command.Init.
    52  func (c *UploadCommand) Init(args []string) error {
    53  	if len(args) == 0 {
    54  		return errors.New("backup filename not specified")
    55  	}
    56  	filename, args := args[0], args[1:]
    57  	if err := cmd.CheckEmpty(args); err != nil {
    58  		return errors.Trace(err)
    59  	}
    60  	c.Filename = filename
    61  	return nil
    62  }
    63  
    64  // Run implements Command.Run.
    65  func (c *UploadCommand) Run(ctx *cmd.Context) error {
    66  	client, err := c.NewAPIClient()
    67  	if err != nil {
    68  		return errors.Trace(err)
    69  	}
    70  	defer client.Close()
    71  
    72  	archive, meta, err := c.getArchive(c.Filename)
    73  	if err != nil {
    74  		return errors.Trace(err)
    75  	}
    76  	defer archive.Close()
    77  
    78  	if c.ShowMeta {
    79  		fmt.Fprintln(ctx.Stdout, "Uploaded metadata:")
    80  		c.dumpMetadata(ctx, meta)
    81  		fmt.Fprintln(ctx.Stdout)
    82  	}
    83  
    84  	// Upload the archive.
    85  	id, err := client.Upload(archive, *meta)
    86  	if err != nil {
    87  		return errors.Trace(err)
    88  	}
    89  
    90  	if c.Quiet {
    91  		fmt.Fprintln(ctx.Stdout, id)
    92  		return nil
    93  	}
    94  
    95  	// Pull the stored metadata.
    96  	stored, err := c.getStoredMetadata(id)
    97  	if err != nil {
    98  		return errors.Trace(err)
    99  	}
   100  
   101  	c.dumpMetadata(ctx, stored)
   102  	return nil
   103  }
   104  
   105  func (c *UploadCommand) getStoredMetadata(id string) (*params.BackupsMetadataResult, error) {
   106  	// TODO(ericsnow) lp-1399722 This should be addressed.
   107  	// There is at least anecdotal evidence that we cannot use an API
   108  	// client for more than a single request. So we use a new client
   109  	// for download.
   110  	client, err := c.NewAPIClient()
   111  	if err != nil {
   112  		return nil, errors.Trace(err)
   113  	}
   114  	defer client.Close()
   115  
   116  	stored, err := client.Info(id)
   117  	return stored, errors.Trace(err)
   118  }
   119  
   120  func (c *UploadCommand) getArchive(filename string) (io.ReadCloser, *params.BackupsMetadataResult, error) {
   121  
   122  	archive, err := os.Open(filename)
   123  	if err != nil {
   124  		return nil, nil, errors.Trace(err)
   125  	}
   126  
   127  	// Extract the metadata.
   128  	ad, err := backups.NewArchiveDataReader(archive)
   129  	if err != nil {
   130  		return nil, nil, errors.Trace(err)
   131  	}
   132  	_, err = archive.Seek(0, os.SEEK_SET)
   133  	if err != nil {
   134  		return nil, nil, errors.Trace(err)
   135  	}
   136  	meta, err := ad.Metadata()
   137  	if err != nil {
   138  		if !errors.IsNotFound(err) {
   139  			return nil, nil, errors.Trace(err)
   140  		}
   141  		meta, err = backups.BuildMetadata(archive)
   142  		if err != nil {
   143  			return nil, nil, errors.Trace(err)
   144  		}
   145  	} else {
   146  		// Make sure the file info is set.
   147  		fileMeta, err := backups.BuildMetadata(archive)
   148  		if err != nil {
   149  			return nil, nil, errors.Trace(err)
   150  		}
   151  		if meta.Size() == int64(0) {
   152  			if err := meta.SetFileInfo(fileMeta.Size(), "", ""); err != nil {
   153  				return nil, nil, errors.Trace(err)
   154  			}
   155  		}
   156  		if meta.Checksum() == "" {
   157  			err := meta.SetFileInfo(0, fileMeta.Checksum(), fileMeta.ChecksumFormat())
   158  			if err != nil {
   159  				return nil, nil, errors.Trace(err)
   160  			}
   161  		}
   162  		if meta.Finished == nil || meta.Finished.IsZero() {
   163  			meta.Finished = fileMeta.Finished
   164  		}
   165  	}
   166  	_, err = archive.Seek(0, os.SEEK_SET)
   167  	if err != nil {
   168  		return nil, nil, errors.Trace(err)
   169  	}
   170  
   171  	// Pack the metadata into a result.
   172  	metaResult := apiserverbackups.ResultFromMetadata(meta)
   173  
   174  	return archive, &metaResult, nil
   175  }