github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/juju/backups/create.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 "time" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "launchpad.net/gnuflag" 15 16 "github.com/juju/juju/state/backups" 17 ) 18 19 const ( 20 notset = backups.FilenamePrefix + "<date>-<time>.tar.gz" 21 downloadWarning = "WARNING: downloading backup archives is recommended; " + 22 "backups stored remotely are not guaranteed to be available" 23 ) 24 25 const createDoc = ` 26 "create" requests that juju create a backup of its state and print the 27 backup's unique ID. You may provide a note to associate with the backup. 28 29 The backup archive and associated metadata are stored remotely by juju. 30 31 The --download option may be used without the --filename option. In 32 that case, the backup archive will be stored in the current working 33 directory with a name matching juju-backup-<date>-<time>.tar.gz. 34 35 WARNING: Remotely stored backups will be lost when the environment is 36 destroyed. Furthermore, the remotely backup is not guaranteed to be 37 available. 38 39 Therefore, you should use the --download or --filename options, or use 40 "juju backups download", to get a local copy of the backup archive. 41 This local copy can then be used to restore an environment even if that 42 environment was already destroyed or is otherwise unavailable. 43 ` 44 45 // CreateCommand is the sub-command for creating a new backup. 46 type CreateCommand struct { 47 CommandBase 48 // Quiet indicates that the full metadata should not be dumped. 49 Quiet bool 50 // NoDownload means the backups archive should not be downloaded. 51 NoDownload bool 52 // Filename is where the backup should be downloaded. 53 Filename string 54 // Notes is the custom message to associated with the new backup. 55 Notes string 56 } 57 58 // Info implements Command.Info. 59 func (c *CreateCommand) Info() *cmd.Info { 60 return &cmd.Info{ 61 Name: "create", 62 Args: "[<notes>]", 63 Purpose: "create a backup", 64 Doc: createDoc, 65 } 66 } 67 68 // SetFlags implements Command.SetFlags. 69 func (c *CreateCommand) SetFlags(f *gnuflag.FlagSet) { 70 f.BoolVar(&c.Quiet, "quiet", false, "do not print the metadata") 71 f.BoolVar(&c.NoDownload, "no-download", false, "do not download the archive") 72 f.StringVar(&c.Filename, "filename", notset, "download to this file") 73 } 74 75 // Init implements Command.Init. 76 func (c *CreateCommand) Init(args []string) error { 77 notes, err := cmd.ZeroOrOneArgs(args) 78 if err != nil { 79 return err 80 } 81 c.Notes = notes 82 83 if c.Filename != notset && c.NoDownload { 84 return errors.Errorf("cannot mix --no-download and --filename") 85 } 86 if c.Filename == "" { 87 return errors.Errorf("missing filename") 88 } 89 90 return nil 91 } 92 93 // Run implements Command.Run. 94 func (c *CreateCommand) Run(ctx *cmd.Context) error { 95 client, err := c.NewAPIClient() 96 if err != nil { 97 return errors.Trace(err) 98 } 99 defer client.Close() 100 101 result, err := client.Create(c.Notes) 102 if err != nil { 103 return errors.Trace(err) 104 } 105 106 if !c.Quiet { 107 if c.NoDownload { 108 fmt.Fprintln(ctx.Stderr, downloadWarning) 109 } 110 c.dumpMetadata(ctx, result) 111 } 112 113 fmt.Fprintln(ctx.Stdout, result.ID) 114 115 // Handle download. 116 filename := c.decideFilename(ctx, c.Filename, result.Started) 117 if filename != "" { 118 if err := c.download(ctx, result.ID, filename); err != nil { 119 return errors.Trace(err) 120 } 121 } 122 123 return nil 124 } 125 126 func (c *CreateCommand) decideFilename(ctx *cmd.Context, filename string, timestamp time.Time) string { 127 if filename != notset { 128 return filename 129 } 130 if c.NoDownload { 131 return "" 132 } 133 134 // Downloading but no filename given, so generate one. 135 return timestamp.Format(backups.FilenameTemplate) 136 } 137 138 func (c *CreateCommand) download(ctx *cmd.Context, id string, filename string) error { 139 fmt.Fprintln(ctx.Stdout, "downloading to "+filename) 140 141 // TODO(ericsnow) lp-1399722 This needs further investigation: 142 // There is at least anecdotal evidence that we cannot use an API 143 // client for more than a single request. So we use a new client 144 // for download. 145 client, err := c.NewAPIClient() 146 if err != nil { 147 return errors.Trace(err) 148 } 149 defer client.Close() 150 151 archive, err := client.Download(id) 152 if err != nil { 153 return errors.Trace(err) 154 } 155 defer archive.Close() 156 157 outfile, err := os.Create(filename) 158 if err != nil { 159 return errors.Trace(err) 160 } 161 defer outfile.Close() 162 163 _, err = io.Copy(outfile, archive) 164 return errors.Trace(err) 165 }