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