1package solver
2
3import (
4	"context"
5
6	digest "github.com/opencontainers/go-digest"
7	"github.com/sirupsen/logrus"
8)
9
10type CacheOpts map[interface{}]interface{}
11
12type cacheOptGetterKey struct{}
13
14func CacheOptGetterOf(ctx context.Context) func(keys ...interface{}) map[interface{}]interface{} {
15	if v := ctx.Value(cacheOptGetterKey{}); v != nil {
16		if getter, ok := v.(func(keys ...interface{}) map[interface{}]interface{}); ok {
17			return getter
18		}
19	}
20	return nil
21}
22
23func withAncestorCacheOpts(ctx context.Context, start *state) context.Context {
24	return context.WithValue(ctx, cacheOptGetterKey{}, func(keys ...interface{}) map[interface{}]interface{} {
25		keySet := make(map[interface{}]struct{})
26		for _, k := range keys {
27			keySet[k] = struct{}{}
28		}
29		values := make(map[interface{}]interface{})
30		walkAncestors(start, func(st *state) bool {
31			if st.clientVertex.Error != "" {
32				// don't use values from cancelled or otherwise error'd vertexes
33				return false
34			}
35			for _, res := range st.op.cacheRes {
36				if res.Opts == nil {
37					continue
38				}
39				for k := range keySet {
40					if v, ok := res.Opts[k]; ok {
41						values[k] = v
42						delete(keySet, k)
43						if len(keySet) == 0 {
44							return true
45						}
46					}
47				}
48			}
49			return false
50		})
51		return values
52	})
53}
54
55func walkAncestors(start *state, f func(*state) bool) {
56	stack := [][]*state{{start}}
57	cache := make(map[digest.Digest]struct{})
58	for len(stack) > 0 {
59		sts := stack[len(stack)-1]
60		if len(sts) == 0 {
61			stack = stack[:len(stack)-1]
62			continue
63		}
64		st := sts[len(sts)-1]
65		stack[len(stack)-1] = sts[:len(sts)-1]
66		if st == nil {
67			continue
68		}
69		if _, ok := cache[st.origDigest]; ok {
70			continue
71		}
72		cache[st.origDigest] = struct{}{}
73		if shouldStop := f(st); shouldStop {
74			return
75		}
76		stack = append(stack, []*state{})
77		for _, parentDgst := range st.clientVertex.Inputs {
78			st.solver.mu.RLock()
79			parent := st.solver.actives[parentDgst]
80			st.solver.mu.RUnlock()
81			if parent == nil {
82				logrus.Warnf("parent %q not found in active job list during cache opt search", parentDgst)
83				continue
84			}
85			stack[len(stack)-1] = append(stack[len(stack)-1], parent)
86		}
87	}
88}
89