github.com/kyma-project/kyma/components/asset-store-controller-manager@v0.0.0-20191203152857-3792b5df17c5/internal/loader/package_loader.go (about)

     1  package loader
     2  
     3  import (
     4  	"archive/tar"
     5  	"archive/zip"
     6  	"compress/gzip"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"strings"
    14  
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  type matcher interface {
    19  	MatchString(s string) bool
    20  }
    21  
    22  func (l *loader) loadPackage(src, name, filter string) (string, []string, error) {
    23  	basePath, err := ioutil.TempDir(l.temporaryDir, name)
    24  	if err != nil {
    25  		return "", nil, err
    26  	}
    27  
    28  	archiveDir, err := ioutil.TempDir(l.temporaryDir, name)
    29  	if err != nil {
    30  		return "", nil, err
    31  	}
    32  	defer l.Clean(archiveDir)
    33  
    34  	fileName := l.fileName(src)
    35  	archivePath := filepath.Join(archiveDir, fileName)
    36  	filterRegexp, err := regexp.Compile(filter)
    37  	if err != nil {
    38  		return "", nil, errors.Wrapf(err, "while compiling filter")
    39  	}
    40  
    41  	unpack, err := l.selectEngine(fileName)
    42  	if err != nil {
    43  		return "", nil, err
    44  	}
    45  
    46  	if err := l.download(archivePath, src); err != nil {
    47  		return "", nil, err
    48  	}
    49  
    50  	files, err := unpack(archivePath, basePath, filterRegexp)
    51  	if err != nil {
    52  		return "", nil, err
    53  	}
    54  
    55  	return basePath, files, nil
    56  }
    57  
    58  func (l *loader) selectEngine(filename string) (func(src, dst string, filter matcher) ([]string, error), error) {
    59  	extension := strings.ToLower(filepath.Ext(filename))
    60  
    61  	switch {
    62  	case extension == ".zip":
    63  		return l.unpackZIP, nil
    64  	case extension == ".tar" || extension == ".tgz" || strings.HasSuffix(strings.ToLower(filename), ".tar.gz"):
    65  		return l.unpackTAR, nil
    66  	}
    67  
    68  	return nil, fmt.Errorf("not supported file type %s", extension)
    69  }
    70  
    71  func (l *loader) unpackTAR(src, dst string, filter matcher) ([]string, error) {
    72  	var filenames []string
    73  	file, err := os.Open(src)
    74  	if err != nil {
    75  		return nil, errors.Wrap(err, "while opening archive")
    76  	}
    77  	defer file.Close()
    78  
    79  	var reader io.Reader
    80  	extension := strings.ToLower(filepath.Ext(src))
    81  	if extension == ".gz" || extension == ".gzip" || extension == ".tgz" {
    82  		reader, err = gzip.NewReader(file)
    83  		if err != nil {
    84  			return nil, errors.Wrap(err, "while creating GZIP reader")
    85  		}
    86  	} else {
    87  		reader = file
    88  	}
    89  
    90  	tarReader := tar.NewReader(reader)
    91  
    92  unpack:
    93  	for {
    94  		header, err := tarReader.Next()
    95  		switch {
    96  		case err == io.EOF:
    97  			break unpack
    98  		case err != nil:
    99  			return nil, errors.Wrap(err, "while unpacking archive")
   100  		}
   101  
   102  		target := filepath.Join(dst, header.Name)
   103  
   104  		switch {
   105  		case !filter.MatchString(header.Name):
   106  			continue
   107  		case header.Typeflag == tar.TypeDir:
   108  			if err := l.createDir(target); err != nil {
   109  				return nil, errors.Wrap(err, "while creating directory")
   110  			}
   111  		case header.Typeflag == tar.TypeReg:
   112  			filenames = append(filenames, header.Name)
   113  
   114  			if err := l.createFile(tarReader, target, header.Mode); err != nil {
   115  				return nil, err
   116  			}
   117  		}
   118  	}
   119  
   120  	return filenames, nil
   121  }
   122  
   123  func (l *loader) unpackZIP(src, dest string, filter matcher) ([]string, error) {
   124  	var filenames []string
   125  
   126  	zipReader, err := zip.OpenReader(src)
   127  	if err != nil {
   128  		return nil, errors.Wrap(err, "while opening archive")
   129  	}
   130  	defer zipReader.Close()
   131  
   132  	for _, file := range zipReader.File {
   133  		if !filter.MatchString(file.Name) {
   134  			continue
   135  		}
   136  
   137  		path, isDir, err := l.handleZIPEntry(file, dest)
   138  		if err != nil {
   139  			return nil, errors.Wrap(err, "while handling ZIP entry")
   140  		}
   141  
   142  		if !isDir {
   143  			filenames = append(filenames, path)
   144  		}
   145  	}
   146  
   147  	return filenames, nil
   148  }
   149  
   150  func (l *loader) handleZIPEntry(file *zip.File, dst string) (string, bool, error) {
   151  	fileReader, err := file.Open()
   152  	if err != nil {
   153  		return "", false, err
   154  	}
   155  	defer fileReader.Close()
   156  
   157  	path := filepath.Join(dst, file.Name)
   158  
   159  	if !strings.HasPrefix(path, filepath.Clean(dst)+string(os.PathSeparator)) {
   160  		return "", false, fmt.Errorf("%s: illegal file path", path)
   161  	}
   162  
   163  	if file.FileInfo().IsDir() {
   164  		if err := l.createDir(path); err != nil {
   165  			return "", false, errors.Wrap(err, "while creating directory")
   166  		}
   167  	} else {
   168  		if err := l.createFile(fileReader, path, int64(file.Mode())); err != nil {
   169  			return "", false, err
   170  		}
   171  	}
   172  
   173  	return file.Name, file.FileInfo().IsDir(), nil
   174  }
   175  
   176  func (l *loader) createFile(src io.Reader, dst string, mode int64) error {
   177  	if err := l.createDir(filepath.Dir(dst)); err != nil {
   178  		return errors.Wrap(err, "while creating directory")
   179  	}
   180  
   181  	outFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(mode))
   182  	if err != nil {
   183  		return errors.Wrap(err, "while opening file")
   184  	}
   185  	defer outFile.Close()
   186  
   187  	_, err = io.Copy(outFile, src)
   188  	if err != nil {
   189  		return errors.Wrap(err, "while copying data to file")
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func (l *loader) createDir(dst string) error {
   196  	return os.MkdirAll(dst, os.ModePerm)
   197  }