github.com/ijc/docker-app@v0.6.1-0.20181012090447-c7ca8bc483fb/loader/loader.go (about)

     1  package loader
     2  
     3  import (
     4  	"io"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/docker/app/types"
    11  	"github.com/docker/docker/pkg/archive"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // LoadFromURL loads a docker app from an URL that should return a single-file app.
    16  func LoadFromURL(url string, ops ...func(*types.App) error) (*types.App, error) {
    17  	resp, err := http.Get(url)
    18  	if err != nil {
    19  		return nil, errors.Wrap(err, "failed to download "+url)
    20  	}
    21  	if resp.Body != nil {
    22  		defer resp.Body.Close()
    23  	}
    24  	if resp.StatusCode != 200 {
    25  		return nil, errors.Errorf("failed to download %s: %s", url, resp.Status)
    26  	}
    27  	return LoadFromSingleFile(url, resp.Body, ops...)
    28  }
    29  
    30  // LoadFromSingleFile loads a docker app from a single-file format (as a reader)
    31  func LoadFromSingleFile(path string, r io.Reader, ops ...func(*types.App) error) (*types.App, error) {
    32  	data, err := ioutil.ReadAll(r)
    33  	if err != nil {
    34  		return nil, errors.Wrap(err, "error reading single-file")
    35  	}
    36  	parts := strings.Split(string(data), types.SingleFileSeparator)
    37  	if len(parts) != 3 {
    38  		return nil, errors.Errorf("malformed single-file application: expected 3 documents, got %d", len(parts))
    39  	}
    40  	// 0. is metadata
    41  	metadata := strings.NewReader(parts[0])
    42  	// 1. is compose
    43  	compose := strings.NewReader(parts[1])
    44  	// 2. is settings
    45  	setting := strings.NewReader(parts[2])
    46  	appOps := append([]func(*types.App) error{
    47  		types.WithComposes(compose),
    48  		types.WithSettings(setting),
    49  		types.Metadata(metadata),
    50  	}, ops...)
    51  	return types.NewApp(path, appOps...)
    52  }
    53  
    54  // LoadFromDirectory loads a docker app from a directory
    55  func LoadFromDirectory(path string, ops ...func(*types.App) error) (*types.App, error) {
    56  	return types.NewAppFromDefaultFiles(path, ops...)
    57  }
    58  
    59  // LoadFromTar loads a docker app from a tarball
    60  func LoadFromTar(tar string, ops ...func(*types.App) error) (*types.App, error) {
    61  	f, err := os.Open(tar)
    62  	if err != nil {
    63  		return nil, errors.Wrap(err, "cannot load app from tar")
    64  	}
    65  	defer f.Close()
    66  	appOps := append(ops, types.WithPath(tar))
    67  	return LoadFromTarReader(f, appOps...)
    68  }
    69  
    70  // LoadFromTarReader loads a docker app from a tarball reader
    71  func LoadFromTarReader(r io.Reader, ops ...func(*types.App) error) (*types.App, error) {
    72  	dir, err := ioutil.TempDir("", "load-from-tar")
    73  	if err != nil {
    74  		return nil, errors.Wrap(err, "cannot load app from tar")
    75  	}
    76  	if err := archive.Untar(r, dir, &archive.TarOptions{
    77  		NoLchown: true,
    78  	}); err != nil {
    79  		originalErr := errors.Wrap(err, "cannot load app from tar")
    80  		if err := os.RemoveAll(dir); err != nil {
    81  			return nil, errors.Wrapf(originalErr, "cannot remove temporary folder : %s", err.Error())
    82  		}
    83  		return nil, originalErr
    84  	}
    85  	appOps := append([]func(*types.App) error{
    86  		types.WithCleanup(func() {
    87  			os.RemoveAll(dir)
    88  		}),
    89  	}, ops...)
    90  	return LoadFromDirectory(dir, appOps...)
    91  }