You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
8.5 KiB
305 lines
8.5 KiB
//
|
|
// DISCLAIMER
|
|
//
|
|
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
//
|
|
// Author Lars Maier
|
|
//
|
|
|
|
package driver
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
type clientBackup struct {
|
|
conn Connection
|
|
}
|
|
|
|
func (c *client) Backup() ClientBackup {
|
|
return &clientBackup{
|
|
conn: c.conn,
|
|
}
|
|
}
|
|
|
|
// Create creates a new backup and returns its id
|
|
func (c *clientBackup) Create(ctx context.Context, opt *BackupCreateOptions) (BackupID, BackupCreateResponse, error) {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/create")
|
|
if err != nil {
|
|
return "", BackupCreateResponse{}, WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
if opt != nil {
|
|
body := struct {
|
|
Label string `json:"label,omitempty"`
|
|
AllowInconsistent bool `json:"allowInconsistent,omitempty"`
|
|
Timeout float64 `json:"timeout,omitempty"`
|
|
}{
|
|
Label: opt.Label,
|
|
AllowInconsistent: opt.AllowInconsistent,
|
|
Timeout: opt.Timeout.Seconds(),
|
|
}
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return "", BackupCreateResponse{}, WithStack(err)
|
|
}
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return "", BackupCreateResponse{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201); err != nil {
|
|
return "", BackupCreateResponse{}, WithStack(err)
|
|
}
|
|
var result struct {
|
|
ID BackupID `json:"id,omitempty"`
|
|
PotentiallyInconsistent bool `json:"potentiallyInconsistent,omitempty"`
|
|
NumberOfFiles uint `json:"nrFiles,omitempty"`
|
|
NumberOfDBServers uint `json:"nrDBServers,omitempty"`
|
|
SizeInBytes uint64 `json:"sizeInBytes,omitempty"`
|
|
CreationTime time.Time `json:"datetime,omitempty"`
|
|
}
|
|
if err := resp.ParseBody("result", &result); err != nil {
|
|
return "", BackupCreateResponse{}, WithStack(err)
|
|
}
|
|
return result.ID, BackupCreateResponse{
|
|
PotentiallyInconsistent: result.PotentiallyInconsistent,
|
|
NumberOfFiles: result.NumberOfFiles,
|
|
NumberOfDBServers: result.NumberOfDBServers,
|
|
SizeInBytes: result.SizeInBytes,
|
|
CreationTime: result.CreationTime,
|
|
}, nil
|
|
}
|
|
|
|
// Delete deletes the backup with given id
|
|
func (c *clientBackup) Delete(ctx context.Context, id BackupID) error {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/delete")
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
body := struct {
|
|
ID BackupID `json:"id,omitempty"`
|
|
}{
|
|
ID: id,
|
|
}
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Restore restores the backup with given id
|
|
func (c *clientBackup) Restore(ctx context.Context, id BackupID, opt *BackupRestoreOptions) error {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/restore")
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
body := struct {
|
|
ID BackupID `json:"id,omitempty"`
|
|
IgnoreVersion bool `json:"ignoreVersion,omitempty"`
|
|
}{
|
|
ID: id,
|
|
}
|
|
if opt != nil {
|
|
body.IgnoreVersion = opt.IgnoreVersion
|
|
}
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
// THIS SHOULD BE 202 ACCEPTED and not OK, because it is not completed when returns (at least for single server)
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// List returns meta data about some/all backups available
|
|
func (c *clientBackup) List(ctx context.Context, opt *BackupListOptions) (map[BackupID]BackupMeta, error) {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/list")
|
|
if err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
if opt != nil {
|
|
req, err = req.SetBody(opt)
|
|
if err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
var result struct {
|
|
List map[BackupID]BackupMeta `json:"list,omitempty"`
|
|
}
|
|
if err := resp.ParseBody("result", &result); err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
return result.List, nil
|
|
}
|
|
|
|
// Upload triggers an upload to the remote repository of backup with id using the given config
|
|
// and returns the job id.
|
|
func (c *clientBackup) Upload(ctx context.Context, id BackupID, remoteRepository string, config interface{}) (BackupTransferJobID, error) {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/upload")
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
body := struct {
|
|
ID BackupID `json:"id,omitempty"`
|
|
RemoteRepo string `json:"remoteRepository,omitempty"`
|
|
Config interface{} `json:"config,omitempty"`
|
|
}{
|
|
ID: id,
|
|
RemoteRepo: remoteRepository,
|
|
Config: config,
|
|
}
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(202); err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
var result struct {
|
|
UploadID BackupTransferJobID `json:"uploadId,omitempty"`
|
|
}
|
|
if err := resp.ParseBody("result", &result); err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
return result.UploadID, nil
|
|
}
|
|
|
|
// Download triggers an download to the remote repository of backup with id using the given config
|
|
// and returns the job id.
|
|
func (c *clientBackup) Download(ctx context.Context, id BackupID, remoteRepository string, config interface{}) (BackupTransferJobID, error) {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/download")
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
body := struct {
|
|
ID BackupID `json:"id,omitempty"`
|
|
RemoteRepo string `json:"remoteRepository,omitempty"`
|
|
Config interface{} `json:"config,omitempty"`
|
|
}{
|
|
ID: id,
|
|
RemoteRepo: remoteRepository,
|
|
Config: config,
|
|
}
|
|
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(202); err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
var result struct {
|
|
DownloadID BackupTransferJobID `json:"downloadId,omitempty"`
|
|
}
|
|
if err := resp.ParseBody("result", &result); err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
return result.DownloadID, nil
|
|
}
|
|
|
|
// Progress returns the progress state of the given Transfer job
|
|
func (c *clientBackup) Progress(ctx context.Context, job BackupTransferJobID) (result BackupTransferProgressReport, error error) {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/upload")
|
|
if err != nil {
|
|
return BackupTransferProgressReport{}, WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
body := struct {
|
|
ID BackupTransferJobID `json:"uploadId,omitempty"`
|
|
}{
|
|
ID: job,
|
|
}
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return BackupTransferProgressReport{}, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return BackupTransferProgressReport{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return BackupTransferProgressReport{}, WithStack(err)
|
|
}
|
|
if err := resp.ParseBody("result", &result); err != nil {
|
|
return BackupTransferProgressReport{}, WithStack(err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Abort aborts the Transfer job if possible
|
|
func (c *clientBackup) Abort(ctx context.Context, job BackupTransferJobID) error {
|
|
req, err := c.conn.NewRequest("POST", "_admin/backup/upload")
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
body := struct {
|
|
ID BackupTransferJobID `json:"uploadId,omitempty"`
|
|
Abort bool `json:"abort,omitempty"`
|
|
}{
|
|
ID: job,
|
|
Abort: true,
|
|
}
|
|
req, err = req.SetBody(body)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(202); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|