github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/image/image.go (about)

     1  package image
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/pkg/errors"
     9  
    10  	"github.com/deislabs/cnab-go/bundle"
    11  	"github.com/docker/cnab-to-oci/relocation"
    12  	"github.com/docker/go/canonical/json"
    13  )
    14  
    15  type AppImage struct {
    16  	*bundle.Bundle
    17  	RelocationMap relocation.ImageRelocationMap
    18  }
    19  
    20  const (
    21  	BundleFilename        = "bundle.json"
    22  	RelocationMapFilename = "relocation-map.json"
    23  )
    24  
    25  // FromBundle returns a RelocatedBundle with an empty relocation map.
    26  func FromBundle(bndl *bundle.Bundle) *AppImage {
    27  	return &AppImage{
    28  		Bundle:        bndl,
    29  		RelocationMap: relocation.ImageRelocationMap{},
    30  	}
    31  }
    32  
    33  // FromFile creates a app image based on the bundle file and relocation map.
    34  func FromFile(filename string) (*AppImage, error) {
    35  	bndl, err := BundleJSON(filename)
    36  	if err != nil {
    37  		return nil, errors.Wrapf(err, "failed to read bundle")
    38  	}
    39  
    40  	relocationMapFileName := filepath.Join(filepath.Dir(filename), RelocationMapFilename)
    41  	relocationMap, err := RelocationMapJSON(relocationMapFileName)
    42  	if err != nil {
    43  		return nil, errors.Wrapf(err, "failed to read relocation map")
    44  	}
    45  
    46  	return &AppImage{
    47  		Bundle:        bndl,
    48  		RelocationMap: relocationMap,
    49  	}, nil
    50  }
    51  
    52  // writeRelocationMap serializes the relocation map and writes it to a file as JSON.
    53  func (b *AppImage) writeRelocationMap(dest string, mode os.FileMode) error {
    54  	d, err := json.MarshalCanonical(b.RelocationMap)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	return ioutil.WriteFile(dest, d, mode)
    59  }
    60  
    61  // Store a bundle with the relocation map as json files.
    62  func (b *AppImage) Store(dir string) error {
    63  	// store bundle.json
    64  	path := filepath.Join(dir, BundleFilename)
    65  	if err := b.WriteFile(path, 0644); err != nil {
    66  		return errors.Wrapf(err, "failed to store bundle")
    67  	}
    68  
    69  	// store relocation map
    70  	relocationMapPath := filepath.Join(dir, RelocationMapFilename)
    71  	if err := b.writeRelocationMap(relocationMapPath, 0644); err != nil {
    72  		return errors.Wrapf(err, "failed to store relocation map")
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  func BundleJSON(bundlePath string) (*bundle.Bundle, error) {
    79  	data, err := ioutil.ReadFile(bundlePath)
    80  	if err != nil {
    81  		return nil, errors.Wrapf(err, "failed to read file %s", bundlePath)
    82  	}
    83  	bndl, err := bundle.Unmarshal(data)
    84  	if err != nil {
    85  		return nil, errors.Wrapf(err, "failed to unmarshal file %s", bundlePath)
    86  	}
    87  	return bndl, nil
    88  }
    89  
    90  func RelocationMapJSON(relocationMapPath string) (relocation.ImageRelocationMap, error) {
    91  	relocationMap := relocation.ImageRelocationMap{}
    92  	_, err := os.Stat(relocationMapPath)
    93  	if os.IsNotExist(err) {
    94  		// it's ok to not have a relocation map, just act as if the file were empty
    95  		return relocationMap, nil
    96  	}
    97  	data, err := ioutil.ReadFile(relocationMapPath)
    98  	if err != nil {
    99  		return nil, errors.Wrapf(err, "failed to read file %s", relocationMapPath)
   100  	}
   101  	if err := json.Unmarshal(data, &relocationMap); err != nil {
   102  		return nil, errors.Wrapf(err, "failed to unmarshal file %s", relocationMapPath)
   103  	}
   104  	return relocationMap, nil
   105  }
   106  
   107  func (b *AppImage) RelocatedImages() map[string]bundle.Image {
   108  	images := b.Images
   109  	for name, def := range images {
   110  		if img, ok := b.RelocationMap[def.Image]; ok {
   111  			def.Image = img
   112  			images[name] = def
   113  		}
   114  	}
   115  
   116  	return images
   117  }