github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/dockerlayer/layer.go (about)

     1  package dockerlayer
     2  
     3  import (
     4  	"context"
     5  	"path"
     6  
     7  	"github.com/go-kit/kit/log"
     8  	"github.com/go-kit/kit/log/level"
     9  	"github.com/mholt/archiver"
    10  	"github.com/pkg/errors"
    11  	"github.com/replicatedhq/libyaml"
    12  	"github.com/replicatedhq/ship/pkg/api"
    13  	"github.com/replicatedhq/ship/pkg/lifecycle/render/docker"
    14  	"github.com/replicatedhq/ship/pkg/lifecycle/render/root"
    15  	"github.com/replicatedhq/ship/pkg/util"
    16  	"github.com/spf13/afero"
    17  	"github.com/spf13/viper"
    18  )
    19  
    20  // An unpacker
    21  type Unpacker struct {
    22  	Logger      log.Logger
    23  	FS          afero.Afero
    24  	Viper       *viper.Viper
    25  	DockerSaver docker.Renderer
    26  	Tar         archiver.Archiver
    27  }
    28  
    29  func TarArchiver() archiver.Archiver {
    30  	return archiver.Tar
    31  }
    32  
    33  func NewUnpacker(
    34  	logger log.Logger,
    35  	dockerStep docker.Renderer,
    36  	fs afero.Afero,
    37  	viper *viper.Viper,
    38  	tar archiver.Archiver,
    39  ) *Unpacker {
    40  
    41  	return &Unpacker{
    42  		Logger:      logger,
    43  		FS:          fs,
    44  		Viper:       viper,
    45  		DockerSaver: dockerStep,
    46  		Tar:         tar,
    47  	}
    48  }
    49  
    50  func (u *Unpacker) Execute(
    51  	rootFs root.Fs,
    52  	asset api.DockerLayerAsset,
    53  	meta api.ReleaseMetadata,
    54  	doWithProgress func(ch chan interface{}, logger log.Logger) error,
    55  	templateContext map[string]interface{},
    56  	configGroups []libyaml.ConfigGroup,
    57  ) func(context.Context) error {
    58  	return func(ctx context.Context) error {
    59  		debug := level.Debug(log.With(u.Logger, "step.type", "render", "render.phase", "execute", "asset.type", "dockerlayer", "dest", asset.Dest, "description", asset.Description))
    60  
    61  		if err := u.mkdirall(rootFs, "tmp")(); err != nil {
    62  			return errors.Wrap(err, "create root tmp path dir")
    63  		}
    64  		savePath, firstPassUnpackPath, basePath, layerPath, err := u.getPaths(asset, rootFs)
    65  		defer rootFs.RemoveAll("tmp") // nolint: errcheck
    66  		if err != nil {
    67  			return errors.Wrap(err, "resolve unpack paths")
    68  		}
    69  
    70  		err = util.IsLegalPath(basePath)
    71  		if err != nil {
    72  			return errors.Wrap(err, "write docker layer")
    73  		}
    74  
    75  		debug.Log(
    76  			"event", "execute",
    77  			"savePath", savePath,
    78  			"firstUnpack", firstPassUnpackPath,
    79  			"basePath", basePath,
    80  			"layerPath", layerPath,
    81  		)
    82  
    83  		return errors.Wrap(u.chain(
    84  			u.save(ctx, rootFs, asset, meta, doWithProgress, savePath, templateContext, configGroups),
    85  			u.mkdirall(rootFs, basePath),
    86  			u.unpack(rootFs, savePath, firstPassUnpackPath),
    87  			u.unpack(rootFs, layerPath, basePath),
    88  		), "execute chain")
    89  	}
    90  }
    91  
    92  func (u *Unpacker) getPaths(asset api.DockerLayerAsset, rootFs root.Fs) (string, string, string, string, error) {
    93  	fail := func(err error) (string, string, string, string, error) { return "", "", "", "", err }
    94  
    95  	saveDir, err := rootFs.TempDir("/tmp", "dockerlayer")
    96  	if err != nil {
    97  		return fail(errors.Wrap(err, "get image save tmpdir"))
    98  	}
    99  
   100  	savePath := path.Join(saveDir, "image.tar")
   101  
   102  	firstPassUnpackPath, err := rootFs.TempDir("/tmp", "dockerlayer")
   103  	if err != nil {
   104  		return fail(errors.Wrap(err, "get unpack tmpdir"))
   105  	}
   106  
   107  	basePath := asset.Dest //TODO enforce that this is a directory
   108  	layerPath := path.Join(firstPassUnpackPath, asset.Layer, "layer.tar")
   109  	return savePath, firstPassUnpackPath, basePath, layerPath, nil
   110  }
   111  
   112  func (u *Unpacker) save(
   113  	ctx context.Context,
   114  	rootFs root.Fs,
   115  	asset api.DockerLayerAsset,
   116  	meta api.ReleaseMetadata,
   117  	doWithProgress func(ch chan interface{}, logger log.Logger) error,
   118  	savePath string,
   119  	templateContext map[string]interface{},
   120  	configGroups []libyaml.ConfigGroup,
   121  ) func() error {
   122  	return func() error {
   123  		return errors.Wrapf(
   124  			u.DockerSaver.Execute(
   125  				rootFs,
   126  				asset.DockerAsset,
   127  				meta,
   128  				doWithProgress,
   129  				savePath,
   130  				templateContext,
   131  				configGroups,
   132  			)(ctx),
   133  			"save image to %s ", savePath)
   134  	}
   135  }
   136  
   137  func (u *Unpacker) unpack(rootFs root.Fs, src string, dest string) func() error {
   138  	return func() error {
   139  		rootPathedSrc := path.Join(rootFs.RootPath, src)
   140  		rootPathedDest := path.Join(rootFs.RootPath, dest)
   141  		return errors.Wrapf(u.Tar.Open(rootPathedSrc, rootPathedDest), "untar %s to %s", rootPathedSrc, rootPathedDest)
   142  	}
   143  }
   144  
   145  func (u *Unpacker) mkdirall(rootFs root.Fs, basePath string) func() error {
   146  	return func() error {
   147  		return errors.Wrapf(rootFs.MkdirAll(basePath, 0755), "mkdirall %s", basePath)
   148  	}
   149  }
   150  
   151  // this is here because it makes sense here, not trying to reinvent any wheels
   152  func (u *Unpacker) chain(fs ...func() error) error {
   153  	for _, f := range fs {
   154  		if err := f(); err != nil {
   155  			return err
   156  		}
   157  	}
   158  	return nil
   159  }