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  }