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 }