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