diff --git a/pkg/restore/backup_extractor.go b/pkg/restore/backup_extractor.go new file mode 100644 index 000000000..827ab7f3b --- /dev/null +++ b/pkg/restore/backup_extractor.go @@ -0,0 +1,100 @@ +/* +Copyright 2019 the Velero contributors. + +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. +*/ + +package restore + +import ( + "archive/tar" + "compress/gzip" + "io" + "path/filepath" + + "github.com/sirupsen/logrus" + + "github.com/heptio/velero/pkg/util/filesystem" +) + +// backupExtractor unzips/extracts a backup tarball to a local +// temp directory. +type backupExtractor struct { + log logrus.FieldLogger + fileSystem filesystem.Interface +} + +// unzipAndExtractBackup extracts a reader on a gzipped tarball to a local temp directory +func (e *backupExtractor) unzipAndExtractBackup(src io.Reader) (string, error) { + gzr, err := gzip.NewReader(src) + if err != nil { + e.log.Infof("error creating gzip reader: %v", err) + return "", err + } + defer gzr.Close() + + return e.readBackup(tar.NewReader(gzr)) +} + +func (e *backupExtractor) readBackup(tarRdr *tar.Reader) (string, error) { + dir, err := e.fileSystem.TempDir("", "") + if err != nil { + e.log.Infof("error creating temp dir: %v", err) + return "", err + } + + for { + header, err := tarRdr.Next() + + if err == io.EOF { + break + } + if err != nil { + e.log.Infof("error reading tar: %v", err) + return "", err + } + + target := filepath.Join(dir, header.Name) + + switch header.Typeflag { + case tar.TypeDir: + err := e.fileSystem.MkdirAll(target, header.FileInfo().Mode()) + if err != nil { + e.log.Infof("mkdirall error: %v", err) + return "", err + } + + case tar.TypeReg: + // make sure we have the directory created + err := e.fileSystem.MkdirAll(filepath.Dir(target), header.FileInfo().Mode()) + if err != nil { + e.log.Infof("mkdirall error: %v", err) + return "", err + } + + // create the file + file, err := e.fileSystem.Create(target) + if err != nil { + return "", err + } + defer file.Close() + + if _, err := io.Copy(file, tarRdr); err != nil { + e.log.Infof("error copying: %v", err) + return "", err + } + } + } + + return dir, nil +} diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 4c906cf6f..17c2610d6 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -17,8 +17,6 @@ limitations under the License. package restore import ( - "archive/tar" - "compress/gzip" go_context "context" "encoding/json" "fmt" @@ -265,6 +263,10 @@ func (kr *kubernetesRestorer) Restore( pvRestorer: pvRestorer, volumeSnapshots: volumeSnapshots, resourceTerminatingTimeout: kr.resourceTerminatingTimeout, + extractor: &backupExtractor{ + log: log, + fileSystem: kr.fileSystem, + }, } return restoreCtx.execute() @@ -351,12 +353,13 @@ type context struct { pvRestorer PVRestorer volumeSnapshots []*volume.Snapshot resourceTerminatingTimeout time.Duration + extractor *backupExtractor } func (ctx *context) execute() (api.RestoreResult, api.RestoreResult) { ctx.log.Infof("Starting restore of backup %s", kube.NamespaceAndName(ctx.backup)) - dir, err := ctx.unzipAndExtractBackup(ctx.backupReader) + dir, err := ctx.extractor.unzipAndExtractBackup(ctx.backupReader) if err != nil { ctx.log.Infof("error unzipping and extracting: %v", err) return api.RestoreResult{}, api.RestoreResult{Ark: []string{err.Error()}} @@ -1283,70 +1286,3 @@ func (ctx *context) unmarshal(filePath string) (*unstructured.Unstructured, erro return &obj, nil } - -// unzipAndExtractBackup extracts a reader on a gzipped tarball to a local temp directory -func (ctx *context) unzipAndExtractBackup(src io.Reader) (string, error) { - gzr, err := gzip.NewReader(src) - if err != nil { - ctx.log.Infof("error creating gzip reader: %v", err) - return "", err - } - defer gzr.Close() - - return ctx.readBackup(tar.NewReader(gzr)) -} - -// readBackup extracts a tar reader to a local directory/file tree within a -// temp directory. -func (ctx *context) readBackup(tarRdr *tar.Reader) (string, error) { - dir, err := ctx.fileSystem.TempDir("", "") - if err != nil { - ctx.log.Infof("error creating temp dir: %v", err) - return "", err - } - - for { - header, err := tarRdr.Next() - - if err == io.EOF { - break - } - if err != nil { - ctx.log.Infof("error reading tar: %v", err) - return "", err - } - - target := filepath.Join(dir, header.Name) - - switch header.Typeflag { - case tar.TypeDir: - err := ctx.fileSystem.MkdirAll(target, header.FileInfo().Mode()) - if err != nil { - ctx.log.Infof("mkdirall error: %v", err) - return "", err - } - - case tar.TypeReg: - // make sure we have the directory created - err := ctx.fileSystem.MkdirAll(filepath.Dir(target), header.FileInfo().Mode()) - if err != nil { - ctx.log.Infof("mkdirall error: %v", err) - return "", err - } - - // create the file - file, err := ctx.fileSystem.Create(target) - if err != nil { - return "", err - } - defer file.Close() - - if _, err := io.Copy(file, tarRdr); err != nil { - ctx.log.Infof("error copying: %v", err) - return "", err - } - } - } - - return dir, nil -}