github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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/utils/featureflag" 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/envcmd" 19 "github.com/juju/juju/feature" 20 statebackups "github.com/juju/juju/state/backups" 21 ) 22 23 var backupsDoc = ` 24 "juju backups" is used to manage backups of the state of a juju environment. 25 ` 26 27 var jesBackupsDoc = ` 28 "juju backups" is used to manage backups of the state of a juju system. 29 Backups are only supported on juju systems, not hosted environments. For 30 more information on juju systems, see: 31 32 juju help juju-systems 33 ` 34 35 const backupsPurpose = "create, manage, and restore backups of juju's state" 36 37 // Command is the top-level command wrapping all backups functionality. 38 type Command struct { 39 cmd.SuperCommand 40 } 41 42 // NewCommand returns a new backups super-command. 43 func NewCommand() cmd.Command { 44 if featureflag.Enabled(feature.JES) { 45 backupsDoc = jesBackupsDoc 46 } 47 48 backupsCmd := Command{ 49 SuperCommand: *cmd.NewSuperCommand( 50 cmd.SuperCommandParams{ 51 Name: "backups", 52 Doc: backupsDoc, 53 UsagePrefix: "juju", 54 Purpose: backupsPurpose, 55 }, 56 ), 57 } 58 backupsCmd.Register(envcmd.Wrap(&CreateCommand{})) 59 backupsCmd.Register(envcmd.Wrap(&InfoCommand{})) 60 backupsCmd.Register(envcmd.Wrap(&ListCommand{})) 61 backupsCmd.Register(envcmd.Wrap(&DownloadCommand{})) 62 backupsCmd.Register(envcmd.Wrap(&UploadCommand{})) 63 backupsCmd.Register(envcmd.Wrap(&RemoveCommand{})) 64 backupsCmd.Register(envcmd.Wrap(&RestoreCommand{})) 65 return &backupsCmd 66 } 67 68 // APIClient represents the backups API client functionality used by 69 // the backups command. 70 type APIClient interface { 71 io.Closer 72 // Create sends an RPC request to create a new backup. 73 Create(notes string) (*params.BackupsMetadataResult, error) 74 // Info gets the backup's metadata. 75 Info(id string) (*params.BackupsMetadataResult, error) 76 // List gets all stored metadata. 77 List() (*params.BackupsListResult, error) 78 // Download pulls the backup archive file. 79 Download(id string) (io.ReadCloser, error) 80 // Upload pushes a backup archive to storage. 81 Upload(ar io.Reader, meta params.BackupsMetadataResult) (string, error) 82 // Remove removes the stored backup. 83 Remove(id string) error 84 // Restore will restore a backup with the given id into the state server. 85 Restore(string, backups.ClientConnection) error 86 // Restore will restore a backup file into the state server. 87 RestoreReader(io.Reader, *params.BackupsMetadataResult, backups.ClientConnection) error 88 } 89 90 // CommandBase is the base type for backups sub-commands. 91 type CommandBase struct { 92 envcmd.EnvCommandBase 93 } 94 95 // NewAPIClient returns a client for the backups api endpoint. 96 func (c *CommandBase) NewAPIClient() (APIClient, error) { 97 return newAPIClient(c) 98 } 99 100 var newAPIClient = func(c *CommandBase) (APIClient, error) { 101 root, err := c.NewAPIRoot() 102 if err != nil { 103 return nil, errors.Trace(err) 104 } 105 return backups.NewClient(root), nil 106 } 107 108 // dumpMetadata writes the formatted backup metadata to stdout. 109 func (c *CommandBase) dumpMetadata(ctx *cmd.Context, result *params.BackupsMetadataResult) { 110 fmt.Fprintf(ctx.Stdout, "backup ID: %q\n", result.ID) 111 fmt.Fprintf(ctx.Stdout, "checksum: %q\n", result.Checksum) 112 fmt.Fprintf(ctx.Stdout, "checksum format: %q\n", result.ChecksumFormat) 113 fmt.Fprintf(ctx.Stdout, "size (B): %d\n", result.Size) 114 fmt.Fprintf(ctx.Stdout, "stored: %v\n", result.Stored) 115 116 fmt.Fprintf(ctx.Stdout, "started: %v\n", result.Started) 117 fmt.Fprintf(ctx.Stdout, "finished: %v\n", result.Finished) 118 fmt.Fprintf(ctx.Stdout, "notes: %q\n", result.Notes) 119 120 fmt.Fprintf(ctx.Stdout, "environment ID: %q\n", result.Environment) 121 fmt.Fprintf(ctx.Stdout, "machine ID: %q\n", result.Machine) 122 fmt.Fprintf(ctx.Stdout, "created on host: %q\n", result.Hostname) 123 fmt.Fprintf(ctx.Stdout, "juju version: %v\n", result.Version) 124 } 125 126 func getArchive(filename string) (rc io.ReadCloser, metaResult *params.BackupsMetadataResult, err error) { 127 defer func() { 128 if err != nil && rc != nil { 129 rc.Close() 130 } 131 }() 132 archive, err := os.Open(filename) 133 rc = archive 134 if err != nil { 135 return nil, nil, errors.Trace(err) 136 } 137 138 // Extract the metadata. 139 ad, err := statebackups.NewArchiveDataReader(archive) 140 if err != nil { 141 return nil, nil, errors.Trace(err) 142 } 143 _, err = archive.Seek(0, os.SEEK_SET) 144 if err != nil { 145 return nil, nil, errors.Trace(err) 146 } 147 meta, err := ad.Metadata() 148 if err != nil { 149 if !errors.IsNotFound(err) { 150 return nil, nil, errors.Trace(err) 151 } 152 meta, err = statebackups.BuildMetadata(archive) 153 if err != nil { 154 return nil, nil, errors.Trace(err) 155 } 156 } 157 // Make sure the file info is set. 158 fileMeta, err := statebackups.BuildMetadata(archive) 159 if err != nil { 160 return nil, nil, errors.Trace(err) 161 } 162 if meta.Size() == int64(0) { 163 if err := meta.SetFileInfo(fileMeta.Size(), "", ""); err != nil { 164 return nil, nil, errors.Trace(err) 165 } 166 } 167 if meta.Checksum() == "" { 168 err := meta.SetFileInfo(0, fileMeta.Checksum(), fileMeta.ChecksumFormat()) 169 if err != nil { 170 return nil, nil, errors.Trace(err) 171 } 172 } 173 if meta.Finished == nil || meta.Finished.IsZero() { 174 meta.Finished = fileMeta.Finished 175 } 176 _, err = archive.Seek(0, os.SEEK_SET) 177 if err != nil { 178 return nil, nil, errors.Trace(err) 179 } 180 181 // Pack the metadata into a result. 182 // TODO(perrito666) change the identity of ResultfromMetadata to 183 // return a pointer. 184 mResult := apiserverbackups.ResultFromMetadata(meta) 185 metaResult = &mResult 186 187 return archive, metaResult, nil 188 }