github.com/weaviate/weaviate@v1.24.6/adapters/handlers/rest/handlers_backup.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package rest 13 14 import ( 15 "github.com/go-openapi/runtime/middleware" 16 "github.com/sirupsen/logrus" 17 "github.com/weaviate/weaviate/adapters/handlers/rest/operations" 18 "github.com/weaviate/weaviate/adapters/handlers/rest/operations/backups" 19 "github.com/weaviate/weaviate/entities/backup" 20 "github.com/weaviate/weaviate/entities/models" 21 "github.com/weaviate/weaviate/usecases/auth/authorization/errors" 22 ubak "github.com/weaviate/weaviate/usecases/backup" 23 "github.com/weaviate/weaviate/usecases/monitoring" 24 ) 25 26 type backupHandlers struct { 27 manager *ubak.Scheduler 28 metricRequestsTotal restApiRequestsTotal 29 } 30 31 // compressionFromCfg transforms model backup config to a backup compression config 32 func compressionFromBCfg(cfg *models.BackupConfig) ubak.Compression { 33 if cfg != nil { 34 if cfg.CPUPercentage == 0 { 35 cfg.CPUPercentage = ubak.DefaultCPUPercentage 36 } 37 38 if cfg.ChunkSize == 0 { 39 cfg.ChunkSize = ubak.DefaultChunkSize 40 } 41 42 if cfg.CompressionLevel == "" { 43 cfg.CompressionLevel = models.BackupConfigCompressionLevelDefaultCompression 44 } 45 46 return ubak.Compression{ 47 CPUPercentage: int(cfg.CPUPercentage), 48 ChunkSize: int(cfg.ChunkSize), 49 Level: parseCompressionLevel(cfg.CompressionLevel), 50 } 51 } 52 53 return ubak.Compression{ 54 Level: ubak.DefaultCompression, 55 CPUPercentage: ubak.DefaultCPUPercentage, 56 ChunkSize: ubak.DefaultChunkSize, 57 } 58 } 59 60 func compressionFromRCfg(cfg *models.RestoreConfig) ubak.Compression { 61 if cfg != nil { 62 if cfg.CPUPercentage == 0 { 63 cfg.CPUPercentage = ubak.DefaultCPUPercentage 64 } 65 66 return ubak.Compression{ 67 CPUPercentage: int(cfg.CPUPercentage), 68 Level: ubak.DefaultCompression, 69 ChunkSize: ubak.DefaultChunkSize, 70 } 71 } 72 73 return ubak.Compression{ 74 Level: ubak.DefaultCompression, 75 CPUPercentage: ubak.DefaultCPUPercentage, 76 ChunkSize: ubak.DefaultChunkSize, 77 } 78 } 79 80 func parseCompressionLevel(l string) ubak.CompressionLevel { 81 switch { 82 case l == models.BackupConfigCompressionLevelBestSpeed: 83 return ubak.BestSpeed 84 case l == models.BackupConfigCompressionLevelBestCompression: 85 return ubak.BestCompression 86 default: 87 return ubak.DefaultCompression 88 } 89 } 90 91 func (s *backupHandlers) createBackup(params backups.BackupsCreateParams, 92 principal *models.Principal, 93 ) middleware.Responder { 94 meta, err := s.manager.Backup(params.HTTPRequest.Context(), principal, &ubak.BackupRequest{ 95 ID: params.Body.ID, 96 Backend: params.Backend, 97 Include: params.Body.Include, 98 Exclude: params.Body.Exclude, 99 Compression: compressionFromBCfg(params.Body.Config), 100 }) 101 if err != nil { 102 s.metricRequestsTotal.logError("", err) 103 switch err.(type) { 104 case errors.Forbidden: 105 return backups.NewBackupsCreateForbidden(). 106 WithPayload(errPayloadFromSingleErr(err)) 107 case backup.ErrUnprocessable: 108 return backups.NewBackupsCreateUnprocessableEntity(). 109 WithPayload(errPayloadFromSingleErr(err)) 110 default: 111 return backups.NewBackupsCreateInternalServerError(). 112 WithPayload(errPayloadFromSingleErr(err)) 113 } 114 } 115 116 s.metricRequestsTotal.logOk("") 117 return backups.NewBackupsCreateOK().WithPayload(meta) 118 } 119 120 func (s *backupHandlers) createBackupStatus(params backups.BackupsCreateStatusParams, 121 principal *models.Principal, 122 ) middleware.Responder { 123 status, err := s.manager.BackupStatus(params.HTTPRequest.Context(), principal, params.Backend, params.ID) 124 if err != nil { 125 s.metricRequestsTotal.logError("", err) 126 switch err.(type) { 127 case errors.Forbidden: 128 return backups.NewBackupsCreateStatusForbidden(). 129 WithPayload(errPayloadFromSingleErr(err)) 130 case backup.ErrUnprocessable: 131 return backups.NewBackupsCreateStatusUnprocessableEntity(). 132 WithPayload(errPayloadFromSingleErr(err)) 133 case backup.ErrNotFound: 134 return backups.NewBackupsCreateStatusNotFound(). 135 WithPayload(errPayloadFromSingleErr(err)) 136 default: 137 return backups.NewBackupsCreateStatusInternalServerError(). 138 WithPayload(errPayloadFromSingleErr(err)) 139 } 140 } 141 142 strStatus := string(status.Status) 143 payload := models.BackupCreateStatusResponse{ 144 Status: &strStatus, 145 ID: params.ID, 146 Path: status.Path, 147 Backend: params.Backend, 148 Error: status.Err, 149 } 150 s.metricRequestsTotal.logOk("") 151 return backups.NewBackupsCreateStatusOK().WithPayload(&payload) 152 } 153 154 func (s *backupHandlers) restoreBackup(params backups.BackupsRestoreParams, 155 principal *models.Principal, 156 ) middleware.Responder { 157 meta, err := s.manager.Restore(params.HTTPRequest.Context(), principal, &ubak.BackupRequest{ 158 ID: params.ID, 159 Backend: params.Backend, 160 Include: params.Body.Include, 161 Exclude: params.Body.Exclude, 162 NodeMapping: params.Body.NodeMapping, 163 Compression: compressionFromRCfg(params.Body.Config), 164 }) 165 if err != nil { 166 s.metricRequestsTotal.logError("", err) 167 switch err.(type) { 168 case errors.Forbidden: 169 return backups.NewBackupsRestoreForbidden(). 170 WithPayload(errPayloadFromSingleErr(err)) 171 case backup.ErrNotFound: 172 return backups.NewBackupsRestoreNotFound(). 173 WithPayload(errPayloadFromSingleErr(err)) 174 case backup.ErrUnprocessable: 175 return backups.NewBackupsRestoreUnprocessableEntity(). 176 WithPayload(errPayloadFromSingleErr(err)) 177 default: 178 return backups.NewBackupsRestoreInternalServerError(). 179 WithPayload(errPayloadFromSingleErr(err)) 180 } 181 } 182 183 s.metricRequestsTotal.logOk("") 184 return backups.NewBackupsRestoreOK().WithPayload(meta) 185 } 186 187 func (s *backupHandlers) restoreBackupStatus(params backups.BackupsRestoreStatusParams, 188 principal *models.Principal, 189 ) middleware.Responder { 190 status, err := s.manager.RestorationStatus( 191 params.HTTPRequest.Context(), principal, params.Backend, params.ID) 192 if err != nil { 193 s.metricRequestsTotal.logError("", err) 194 switch err.(type) { 195 case errors.Forbidden: 196 return backups.NewBackupsRestoreForbidden(). 197 WithPayload(errPayloadFromSingleErr(err)) 198 case backup.ErrNotFound: 199 return backups.NewBackupsRestoreNotFound(). 200 WithPayload(errPayloadFromSingleErr(err)) 201 case backup.ErrUnprocessable: 202 return backups.NewBackupsRestoreUnprocessableEntity(). 203 WithPayload(errPayloadFromSingleErr(err)) 204 default: 205 return backups.NewBackupsRestoreInternalServerError(). 206 WithPayload(errPayloadFromSingleErr(err)) 207 } 208 } 209 strStatus := string(status.Status) 210 payload := models.BackupRestoreStatusResponse{ 211 Status: &strStatus, 212 ID: params.ID, 213 Path: status.Path, 214 Backend: params.Backend, 215 Error: status.Err, 216 } 217 s.metricRequestsTotal.logOk("") 218 return backups.NewBackupsRestoreStatusOK().WithPayload(&payload) 219 } 220 221 func setupBackupHandlers(api *operations.WeaviateAPI, 222 scheduler *ubak.Scheduler, metrics *monitoring.PrometheusMetrics, logger logrus.FieldLogger, 223 ) { 224 h := &backupHandlers{scheduler, newBackupRequestsTotal(metrics, logger)} 225 api.BackupsBackupsCreateHandler = backups. 226 BackupsCreateHandlerFunc(h.createBackup) 227 api.BackupsBackupsCreateStatusHandler = backups. 228 BackupsCreateStatusHandlerFunc(h.createBackupStatus) 229 api.BackupsBackupsRestoreHandler = backups. 230 BackupsRestoreHandlerFunc(h.restoreBackup) 231 api.BackupsBackupsRestoreStatusHandler = backups. 232 BackupsRestoreStatusHandlerFunc(h.restoreBackupStatus) 233 } 234 235 type backupRequestsTotal struct { 236 *restApiRequestsTotalImpl 237 } 238 239 func newBackupRequestsTotal(metrics *monitoring.PrometheusMetrics, logger logrus.FieldLogger) restApiRequestsTotal { 240 return &backupRequestsTotal{ 241 restApiRequestsTotalImpl: &restApiRequestsTotalImpl{newRequestsTotalMetric(metrics, "rest"), "rest", "backup", logger}, 242 } 243 } 244 245 func (e *backupRequestsTotal) logError(className string, err error) { 246 switch err.(type) { 247 case errors.Forbidden: 248 e.logUserError(className) 249 case backup.ErrUnprocessable, backup.ErrNotFound: 250 e.logUserError(className) 251 default: 252 e.logServerError(className, err) 253 } 254 }