github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "github.com/juju/errors" 8 "github.com/juju/mgo/v3" 9 "github.com/juju/names/v5" 10 11 "github.com/juju/juju/apiserver/authentication" 12 "github.com/juju/juju/apiserver/common" 13 apiservererrors "github.com/juju/juju/apiserver/errors" 14 "github.com/juju/juju/apiserver/facade" 15 "github.com/juju/juju/controller" 16 corebase "github.com/juju/juju/core/base" 17 "github.com/juju/juju/core/permission" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/rpc/params" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/state/backups" 22 ) 23 24 // Backend exposes state.State functionality needed by the backups Facade. 25 type Backend interface { 26 IsController() bool 27 Machine(id string) (Machine, error) 28 MachineBase(id string) (corebase.Base, error) 29 MongoSession() *mgo.Session 30 ModelTag() names.ModelTag 31 ModelType() state.ModelType 32 ControllerTag() names.ControllerTag 33 ModelConfig() (*config.Config, error) 34 ControllerConfig() (controller.Config, error) 35 StateServingInfo() (controller.StateServingInfo, error) 36 ControllerNodes() ([]state.ControllerNode, error) 37 } 38 39 // API provides backup-specific API methods. 40 type API struct { 41 backend Backend 42 paths *backups.Paths 43 44 // machineID is the ID of the machine where the API server is running. 45 machineID string 46 } 47 48 // NewAPI creates a new instance of the Backups API facade. 49 func NewAPI(backend Backend, resources facade.Resources, authorizer facade.Authorizer) (*API, error) { 50 err := authorizer.HasPermission(permission.SuperuserAccess, backend.ControllerTag()) 51 if err != nil && 52 !errors.Is(err, authentication.ErrorEntityMissingPermission) && 53 !errors.Is(err, errors.NotFound) { 54 return nil, errors.Trace(err) 55 } 56 isControllerAdmin := err == nil 57 58 if !authorizer.AuthClient() || !isControllerAdmin { 59 return nil, apiservererrors.ErrPerm 60 } 61 62 // For now, backup operations are only permitted on the controller model. 63 if !backend.IsController() { 64 return nil, errors.New("backups are only supported from the controller model\nUse juju switch to select the controller model") 65 } 66 67 if backend.ModelType() == state.ModelTypeCAAS { 68 return nil, errors.NotSupportedf("backups on kubernetes controllers") 69 } 70 71 // Get the backup paths. 72 dataDir, err := extractResourceValue(resources, "dataDir") 73 if err != nil { 74 return nil, errors.Trace(err) 75 } 76 logsDir, err := extractResourceValue(resources, "logDir") 77 if err != nil { 78 return nil, errors.Trace(err) 79 } 80 81 modelConfig, err := backend.ModelConfig() 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 backupDir := backups.BackupDirToUse(modelConfig.BackupDir()) 86 paths := backups.Paths{ 87 BackupDir: backupDir, 88 DataDir: dataDir, 89 LogsDir: logsDir, 90 } 91 92 // Build the API. 93 machineID, err := extractResourceValue(resources, "machineID") 94 if err != nil { 95 return nil, errors.Trace(err) 96 } 97 b := API{ 98 backend: backend, 99 paths: &paths, 100 machineID: machineID, 101 } 102 return &b, nil 103 } 104 105 func extractResourceValue(resources facade.Resources, key string) (string, error) { 106 res := resources.Get(key) 107 strRes, ok := res.(common.StringResource) 108 if !ok { 109 if res == nil { 110 strRes = "" 111 } else { 112 return "", errors.Errorf("invalid %s resource: %v", key, res) 113 } 114 } 115 return strRes.String(), nil 116 } 117 118 var newBackups = backups.NewBackups 119 120 // CreateResult updates the result with the information in the 121 // metadata value. 122 func CreateResult(meta *backups.Metadata, filename string) params.BackupsMetadataResult { 123 var result params.BackupsMetadataResult 124 125 result.ID = meta.ID() 126 127 result.Checksum = meta.Checksum() 128 result.ChecksumFormat = meta.ChecksumFormat() 129 result.Size = meta.Size() 130 if meta.Stored() != nil { 131 result.Stored = *(meta.Stored()) 132 } 133 134 result.Started = meta.Started 135 if meta.Finished != nil { 136 result.Finished = *meta.Finished 137 } 138 result.Notes = meta.Notes 139 140 result.Model = meta.Origin.Model 141 result.Machine = meta.Origin.Machine 142 result.Hostname = meta.Origin.Hostname 143 result.Version = meta.Origin.Version 144 result.Base = meta.Origin.Base 145 146 result.ControllerUUID = meta.Controller.UUID 147 result.FormatVersion = meta.FormatVersion 148 result.HANodes = meta.Controller.HANodes 149 result.ControllerMachineID = meta.Controller.MachineID 150 result.ControllerMachineInstanceID = meta.Controller.MachineInstanceID 151 result.Filename = filename 152 153 return result 154 }