1package solver 2 3import ( 4 "context" 5 6 digest "github.com/opencontainers/go-digest" 7) 8 9type exporter struct { 10 k *CacheKey 11 records []*CacheRecord 12 record *CacheRecord 13 14 res []CacheExporterRecord 15 edge *edge // for secondaryExporters 16 override *bool 17} 18 19func addBacklinks(t CacheExporterTarget, rec CacheExporterRecord, cm *cacheManager, id string, bkm map[string]CacheExporterRecord) (CacheExporterRecord, error) { 20 if rec == nil { 21 var ok bool 22 rec, ok = bkm[id] 23 if ok && rec != nil { 24 return rec, nil 25 } 26 _ = ok 27 } 28 bkm[id] = nil 29 if err := cm.backend.WalkBacklinks(id, func(id string, link CacheInfoLink) error { 30 if rec == nil { 31 rec = t.Add(link.Digest) 32 } 33 r, ok := bkm[id] 34 if !ok { 35 var err error 36 r, err = addBacklinks(t, nil, cm, id, bkm) 37 if err != nil { 38 return err 39 } 40 } 41 if r != nil { 42 rec.LinkFrom(r, int(link.Input), link.Selector.String()) 43 } 44 return nil 45 }); err != nil { 46 return nil, err 47 } 48 if rec == nil { 49 rec = t.Add(digest.Digest(id)) 50 } 51 bkm[id] = rec 52 return rec, nil 53} 54 55type backlinkT struct{} 56 57var backlinkKey = backlinkT{} 58 59func (e *exporter) ExportTo(ctx context.Context, t CacheExporterTarget, opt CacheExportOpt) ([]CacheExporterRecord, error) { 60 var bkm map[string]CacheExporterRecord 61 62 if bk := ctx.Value(backlinkKey); bk == nil { 63 bkm = map[string]CacheExporterRecord{} 64 ctx = context.WithValue(ctx, backlinkKey, bkm) 65 } else { 66 bkm = bk.(map[string]CacheExporterRecord) 67 } 68 69 if t.Visited(e) { 70 return e.res, nil 71 } 72 t.Visit(e) 73 74 deps := e.k.Deps() 75 76 type expr struct { 77 r CacheExporterRecord 78 selector digest.Digest 79 } 80 81 rec := t.Add(rootKey(e.k.Digest(), e.k.Output())) 82 allRec := []CacheExporterRecord{rec} 83 84 addRecord := true 85 86 if e.override != nil { 87 addRecord = *e.override 88 } 89 90 if e.record == nil && len(e.k.Deps()) > 0 { 91 e.record = getBestResult(e.records) 92 } 93 94 var remote *Remote 95 if v := e.record; v != nil && len(e.k.Deps()) > 0 && addRecord { 96 cm := v.cacheManager 97 key := cm.getID(v.key) 98 res, err := cm.backend.Load(key, v.ID) 99 if err != nil { 100 return nil, err 101 } 102 103 remote, err = cm.results.LoadRemote(ctx, res, opt.Session) 104 if err != nil { 105 return nil, err 106 } 107 108 if remote == nil && opt.Mode != CacheExportModeRemoteOnly { 109 res, err := cm.results.Load(ctx, res) 110 if err != nil { 111 return nil, err 112 } 113 remote, err = opt.Convert(ctx, res) 114 if err != nil { 115 return nil, err 116 } 117 res.Release(context.TODO()) 118 } 119 120 if remote != nil { 121 for _, rec := range allRec { 122 rec.AddResult(v.CreatedAt, remote) 123 } 124 } 125 } 126 127 if remote != nil && opt.Mode == CacheExportModeMin { 128 opt.Mode = CacheExportModeRemoteOnly 129 } 130 131 srcs := make([][]expr, len(deps)) 132 133 for i, deps := range deps { 134 for _, dep := range deps { 135 recs, err := dep.CacheKey.Exporter.ExportTo(ctx, t, opt) 136 if err != nil { 137 return nil, nil 138 } 139 for _, r := range recs { 140 srcs[i] = append(srcs[i], expr{r: r, selector: dep.Selector}) 141 } 142 } 143 } 144 145 if e.edge != nil { 146 for _, de := range e.edge.secondaryExporters { 147 recs, err := de.cacheKey.CacheKey.Exporter.ExportTo(ctx, t, opt) 148 if err != nil { 149 return nil, nil 150 } 151 for _, r := range recs { 152 srcs[de.index] = append(srcs[de.index], expr{r: r, selector: de.cacheKey.Selector}) 153 } 154 } 155 } 156 157 for i, srcs := range srcs { 158 for _, src := range srcs { 159 rec.LinkFrom(src.r, i, src.selector.String()) 160 } 161 } 162 163 for cm, id := range e.k.ids { 164 if _, err := addBacklinks(t, rec, cm, id, bkm); err != nil { 165 return nil, err 166 } 167 } 168 169 if v := e.record; v != nil && len(deps) == 0 { 170 cm := v.cacheManager 171 key := cm.getID(v.key) 172 if err := cm.backend.WalkIDsByResult(v.ID, func(id string) error { 173 if id == key { 174 return nil 175 } 176 allRec = append(allRec, t.Add(digest.Digest(id))) 177 return nil 178 }); err != nil { 179 return nil, err 180 } 181 } 182 183 e.res = allRec 184 185 return e.res, nil 186} 187 188func getBestResult(records []*CacheRecord) *CacheRecord { 189 var rec *CacheRecord 190 for _, r := range records { 191 if rec == nil || rec.CreatedAt.Before(r.CreatedAt) || (rec.CreatedAt.Equal(r.CreatedAt) && rec.Priority < r.Priority) { 192 rec = r 193 } 194 } 195 return rec 196} 197 198type mergedExporter struct { 199 exporters []CacheExporter 200} 201 202func (e *mergedExporter) ExportTo(ctx context.Context, t CacheExporterTarget, opt CacheExportOpt) (er []CacheExporterRecord, err error) { 203 for _, e := range e.exporters { 204 r, err := e.ExportTo(ctx, t, opt) 205 if err != nil { 206 return nil, err 207 } 208 er = append(er, r...) 209 } 210 return 211} 212