get.porter.sh/porter@v1.3.0/pkg/cache/cached_bundle.go (about) 1 package cache 2 3 import ( 4 "crypto/md5" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "path/filepath" 9 10 "get.porter.sh/porter/pkg/cnab" 11 "get.porter.sh/porter/pkg/config" 12 "get.porter.sh/porter/pkg/encoding" 13 "get.porter.sh/porter/pkg/portercontext" 14 "github.com/cnabio/cnab-to-oci/relocation" 15 ) 16 17 // CachedBundle represents a bundle pulled from a registry that has been cached to 18 // the filesystem. 19 type CachedBundle struct { 20 // cacheDir is the cache directory for the bundle (not the general cache directory) 21 cacheDir string 22 23 // BundleReference contains common bundle metadata, such as the definition. 24 cnab.BundleReference 25 26 // BundlePath is the location of the bundle.json in the cache. 27 BundlePath string 28 29 // ManifestPath is the optional location of the porter.yaml in the cache. 30 ManifestPath string 31 32 // RelocationFilePath is the optional location of the relocation file in the cache. 33 RelocationFilePath string 34 } 35 36 // GetBundleID is the unique ID of the cached bundle. 37 func (cb *CachedBundle) GetBundleID() string { 38 // hash the tag, tags have characters that won't work as part of a path 39 // so hashing here to get a path friendly name 40 bid := md5.Sum([]byte(cb.Reference.String())) 41 return hex.EncodeToString(bid[:]) 42 } 43 44 // SetCacheDir sets the bundle specific cache directory based on the given Porter cache directory. 45 func (cb *CachedBundle) SetCacheDir(porterCacheDir string) { 46 cb.cacheDir = filepath.Join(porterCacheDir, cb.GetBundleID()) 47 } 48 49 // BuildBundlePath generates the potential location of the bundle.json, if it existed. 50 func (cb *CachedBundle) BuildBundlePath() string { 51 return filepath.Join(cb.cacheDir, "cnab", "bundle.json") 52 } 53 54 // BuildRelocationFilePath generates the potential location of the relocation file, if it existed. 55 func (cb *CachedBundle) BuildRelocationFilePath() string { 56 return filepath.Join(cb.cacheDir, "cnab", "relocation-mapping.json") 57 } 58 59 // BuildManifestPath generates the potential location of the manifest, if it existed. 60 func (cb *CachedBundle) BuildManifestPath() string { 61 return filepath.Join(cb.cacheDir, config.Name) 62 } 63 64 // BuildMetadataPath generates the location of the cache metadata. 65 func (cb *CachedBundle) BuildMetadataPath() string { 66 return filepath.Join(cb.cacheDir, "metadata.json") 67 } 68 69 // Load starts from the bundle tag, and hydrates the cached bundle from the cache. 70 func (cb *CachedBundle) Load(cxt *portercontext.Context) (bool, error) { 71 // Check that the bundle exists 72 cb.BundlePath = cb.BuildBundlePath() 73 metaPath := cb.BuildMetadataPath() 74 metaExists, err := cxt.FileSystem.Exists(metaPath) 75 if err != nil { 76 return false, fmt.Errorf("unable to access bundle metadata %s at %s: %w", cb.Reference, metaPath, err) 77 } 78 if !metaExists { 79 // consider this a miss, recache with the metadata 80 return false, nil 81 } 82 var meta Metadata 83 err = encoding.UnmarshalFile(cxt.FileSystem, metaPath, &meta) 84 if err != nil { 85 return false, fmt.Errorf("unable to parse cached bundle metadata %s at %s: %w", cb.Reference, metaPath, err) 86 } 87 cb.Digest = meta.Digest 88 89 // Check for the optional relocation mapping next to it 90 reloPath := cb.BuildRelocationFilePath() 91 reloExists, err := cxt.FileSystem.Exists(reloPath) 92 if err != nil { 93 return true, fmt.Errorf("unable to read relocation mapping %s at %s: %w", cb.Reference, reloPath, err) 94 } 95 if reloExists { 96 cb.RelocationFilePath = reloPath 97 } 98 99 // Check for the optional manifest 100 manifestPath := cb.BuildManifestPath() 101 manifestExists, err := cxt.FileSystem.Exists(manifestPath) 102 if err != nil { 103 return true, fmt.Errorf("unable to read manifest %s at %s: %w", cb.Reference, manifestPath, err) 104 } 105 if manifestExists { 106 cb.ManifestPath = manifestPath 107 } 108 109 bun, err := cnab.LoadBundle(cxt, cb.BundlePath) 110 if err != nil { 111 return true, fmt.Errorf("unable to parse cached bundle file at %s: %w", cb.BundlePath, err) 112 } 113 cb.Definition = bun 114 115 if cb.RelocationFilePath != "" { 116 data, err := cxt.FileSystem.ReadFile(cb.RelocationFilePath) 117 if err != nil { 118 return true, fmt.Errorf("unable to read cached relocation file at %s: %w", cb.RelocationFilePath, err) 119 } 120 121 reloMap := relocation.ImageRelocationMap{} 122 err = json.Unmarshal(data, &reloMap) 123 if err != nil { 124 return true, fmt.Errorf("unable to parse cached relocation file at %s: %w", cb.RelocationFilePath, err) 125 } 126 cb.RelocationMap = reloMap 127 } 128 129 return true, nil 130 }