github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/cache/cache_opts.go (about) 1 package cache 2 3 import ( 4 "encoding/csv" 5 "fmt" 6 "path/filepath" 7 "strings" 8 9 "github.com/pkg/errors" 10 ) 11 12 type Format int 13 type CacheInfo struct { 14 Format Format 15 Source string 16 } 17 18 type CacheOpts struct { 19 Build CacheInfo 20 Launch CacheInfo 21 Kaniko CacheInfo 22 } 23 24 const ( 25 CacheVolume Format = iota 26 CacheImage 27 CacheBind 28 ) 29 30 func (f Format) String() string { 31 switch f { 32 case CacheImage: 33 return "image" 34 case CacheVolume: 35 return "volume" 36 case CacheBind: 37 return "bind" 38 } 39 return "" 40 } 41 42 func (c *CacheInfo) SourceName() string { 43 switch c.Format { 44 case CacheImage: 45 fallthrough 46 case CacheVolume: 47 return "name" 48 case CacheBind: 49 return "source" 50 } 51 return "" 52 } 53 54 func (c *CacheOpts) Set(value string) error { 55 csvReader := csv.NewReader(strings.NewReader(value)) 56 csvReader.Comma = ';' 57 fields, err := csvReader.Read() 58 if err != nil { 59 return err 60 } 61 62 cache := &c.Build 63 for _, field := range fields { 64 parts := strings.SplitN(field, "=", 2) 65 if len(parts) != 2 { 66 return errors.Errorf("invalid field '%s' must be a key=value pair", field) 67 } 68 key := strings.ToLower(parts[0]) 69 value := strings.ToLower(parts[1]) 70 if key == "type" { 71 switch value { 72 case "build": 73 cache = &c.Build 74 case "launch": 75 cache = &c.Launch 76 default: 77 return errors.Errorf("invalid cache type '%s'", value) 78 } 79 break 80 } 81 } 82 83 for _, field := range fields { 84 parts := strings.SplitN(field, "=", 2) 85 if len(parts) != 2 { 86 return errors.Errorf("invalid field '%s' must be a key=value pair", field) 87 } 88 key := strings.ToLower(parts[0]) 89 value := strings.ToLower(parts[1]) 90 switch key { 91 case "format": 92 switch value { 93 case "image": 94 cache.Format = CacheImage 95 case "volume": 96 cache.Format = CacheVolume 97 case "bind": 98 cache.Format = CacheBind 99 default: 100 return errors.Errorf("invalid cache format '%s'", value) 101 } 102 case "name": 103 cache.Source = value 104 case "source": 105 cache.Source = value 106 } 107 } 108 109 err = sanitize(c) 110 if err != nil { 111 return err 112 } 113 return nil 114 } 115 116 func (c *CacheOpts) String() string { 117 var cacheFlag string 118 cacheFlag = fmt.Sprintf("type=build;format=%s;", c.Build.Format.String()) 119 if c.Build.Source != "" { 120 cacheFlag += fmt.Sprintf("%s=%s;", c.Build.SourceName(), c.Build.Source) 121 } 122 123 cacheFlag += fmt.Sprintf("type=launch;format=%s;", c.Launch.Format.String()) 124 if c.Launch.Source != "" { 125 cacheFlag += fmt.Sprintf("%s=%s;", c.Launch.SourceName(), c.Launch.Source) 126 } 127 128 return cacheFlag 129 } 130 131 func (c *CacheOpts) Type() string { 132 return "cache" 133 } 134 135 func sanitize(c *CacheOpts) error { 136 for _, v := range []CacheInfo{c.Build, c.Launch} { 137 // volume cache name can be auto-generated 138 if v.Format != CacheVolume && v.Source == "" { 139 return errors.Errorf("cache '%s' is required", v.SourceName()) 140 } 141 } 142 143 var ( 144 resolvedPath string 145 err error 146 ) 147 if c.Build.Format == CacheBind { 148 if resolvedPath, err = filepath.Abs(c.Build.Source); err != nil { 149 return errors.Wrap(err, "resolve absolute path") 150 } 151 c.Build.Source = filepath.Join(resolvedPath, "build-cache") 152 } 153 if c.Launch.Format == CacheBind { 154 if resolvedPath, err = filepath.Abs(c.Launch.Source); err != nil { 155 return errors.Wrap(err, "resolve absolute path") 156 } 157 c.Launch.Source = filepath.Join(resolvedPath, "launch-cache") 158 } 159 return nil 160 }