github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/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 "io" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "gopkg.in/juju/names.v2" 12 "gopkg.in/mgo.v2" 13 14 "github.com/juju/juju/apiserver/common" 15 "github.com/juju/juju/apiserver/facade" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/controller" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/permission" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/state/backups" 22 ) 23 24 var logger = loggo.GetLogger("juju.apiserver.backups") 25 26 // Backend exposes state.State functionality needed by the backups Facade. 27 type Backend interface { 28 IsController() bool 29 Machine(id string) (*state.Machine, error) 30 MachineSeries(id string) (string, error) 31 MongoSession() *mgo.Session 32 MongoVersion() (string, error) 33 ModelTag() names.ModelTag 34 ControllerTag() names.ControllerTag 35 ModelConfig() (*config.Config, error) 36 ControllerConfig() (controller.Config, error) 37 StateServingInfo() (state.StateServingInfo, error) 38 RestoreInfo() *state.RestoreInfo 39 } 40 41 // API provides backup-specific API methods. 42 type API struct { 43 backend Backend 44 paths *backups.Paths 45 46 // machineID is the ID of the machine where the API server is running. 47 machineID string 48 } 49 50 // APIv2 serves backup-specific API methods for version 2. 51 type APIv2 struct { 52 *API 53 } 54 55 func NewAPIv2(backend Backend, resources facade.Resources, authorizer facade.Authorizer) (*APIv2, error) { 56 api, err := NewAPI(backend, resources, authorizer) 57 if err != nil { 58 return nil, errors.Trace(err) 59 } 60 return &APIv2{api}, nil 61 } 62 63 // NewAPI creates a new instance of the Backups API facade. 64 func NewAPI(backend Backend, resources facade.Resources, authorizer facade.Authorizer) (*API, error) { 65 isControllerAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, backend.ControllerTag()) 66 if err != nil && !errors.IsNotFound(err) { 67 return nil, errors.Trace(err) 68 } 69 70 if !authorizer.AuthClient() || !isControllerAdmin { 71 return nil, common.ErrPerm 72 } 73 74 // For now, backup operations are only permitted on the controller model. 75 if !backend.IsController() { 76 return nil, errors.New("backups are only supported from the controller model\nUse juju switch to select the controller model") 77 } 78 79 // Get the backup paths. 80 dataDir, err := extractResourceValue(resources, "dataDir") 81 if err != nil { 82 return nil, errors.Trace(err) 83 } 84 logsDir, err := extractResourceValue(resources, "logDir") 85 if err != nil { 86 return nil, errors.Trace(err) 87 } 88 89 config, err := backend.ModelConfig() 90 if err != nil { 91 return nil, errors.Trace(err) 92 } 93 backupDir := config.BackupDir() 94 95 paths := backups.Paths{ 96 BackupDir: backupDir, 97 DataDir: dataDir, 98 LogsDir: logsDir, 99 } 100 101 // Build the API. 102 machineID, err := extractResourceValue(resources, "machineID") 103 if err != nil { 104 return nil, errors.Trace(err) 105 } 106 b := API{ 107 backend: backend, 108 paths: &paths, 109 machineID: machineID, 110 } 111 return &b, nil 112 } 113 114 func extractResourceValue(resources facade.Resources, key string) (string, error) { 115 res := resources.Get(key) 116 strRes, ok := res.(common.StringResource) 117 if !ok { 118 if res == nil { 119 strRes = "" 120 } else { 121 return "", errors.Errorf("invalid %s resource: %v", key, res) 122 } 123 } 124 return strRes.String(), nil 125 } 126 127 var newBackups = func(backend Backend) (backups.Backups, io.Closer) { 128 stor := backups.NewStorage(backend) 129 return backups.NewBackups(stor), stor 130 } 131 132 // CreateResult updates the result with the information in the 133 // metadata value. 134 func CreateResult(meta *backups.Metadata, filename string) params.BackupsMetadataResult { 135 var result params.BackupsMetadataResult 136 137 result.ID = meta.ID() 138 139 result.Checksum = meta.Checksum() 140 result.ChecksumFormat = meta.ChecksumFormat() 141 result.Size = meta.Size() 142 if meta.Stored() != nil { 143 result.Stored = *(meta.Stored()) 144 } 145 146 result.Started = meta.Started 147 if meta.Finished != nil { 148 result.Finished = *meta.Finished 149 } 150 result.Notes = meta.Notes 151 152 result.Model = meta.Origin.Model 153 result.Machine = meta.Origin.Machine 154 result.Hostname = meta.Origin.Hostname 155 result.Version = meta.Origin.Version 156 result.Series = meta.Origin.Series 157 158 // TODO(wallyworld) - remove these ASAP 159 // These are only used by the restore CLI when re-bootstrapping. 160 // We will use a better solution but the way restore currently 161 // works, we need them and they are no longer available via 162 // bootstrap config. We will need to fix how re-bootstrap deals 163 // with these keys to address the issue. 164 result.CACert = meta.CACert 165 result.CAPrivateKey = meta.CAPrivateKey 166 result.Filename = filename 167 168 return result 169 } 170 171 // MetadataFromResult returns a new Metadata based on the result. The ID 172 // of the metadata is not set. Call meta.SetID() if that is desired. 173 // Likewise with Stored and meta.SetStored(). 174 func MetadataFromResult(result params.BackupsMetadataResult) *backups.Metadata { 175 meta := backups.NewMetadata() 176 meta.Started = result.Started 177 if !result.Finished.IsZero() { 178 meta.Finished = &result.Finished 179 } 180 meta.Origin.Model = result.Model 181 meta.Origin.Machine = result.Machine 182 meta.Origin.Hostname = result.Hostname 183 meta.Origin.Version = result.Version 184 meta.Origin.Series = result.Series 185 meta.Notes = result.Notes 186 meta.SetFileInfo(result.Size, result.Checksum, result.ChecksumFormat) 187 return meta 188 }