summaryrefslogtreecommitdiff
path: root/vendor/github.com/moby/buildkit/cache/remotecache/local/local.go
blob: 18c73364c03b07ed04b819a8493a9f00e94e5687 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package local

import (
	"context"
	"strconv"
	"time"

	"github.com/containerd/containerd/content"
	"github.com/moby/buildkit/cache/remotecache"
	"github.com/moby/buildkit/session"
	sessioncontent "github.com/moby/buildkit/session/content"
	"github.com/moby/buildkit/util/compression"
	digest "github.com/opencontainers/go-digest"
	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

const (
	attrDigest           = "digest"
	attrSrc              = "src"
	attrDest             = "dest"
	attrOCIMediatypes    = "oci-mediatypes"
	contentStoreIDPrefix = "local:"
	attrLayerCompression = "compression"
	attrForceCompression = "force-compression"
	attrCompressionLevel = "compression-level"
)

// ResolveCacheExporterFunc for "local" cache exporter.
func ResolveCacheExporterFunc(sm *session.Manager) remotecache.ResolveCacheExporterFunc {
	return func(ctx context.Context, g session.Group, attrs map[string]string) (remotecache.Exporter, error) {
		store := attrs[attrDest]
		if store == "" {
			return nil, errors.New("local cache exporter requires dest")
		}
		compressionConfig, err := attrsToCompression(attrs)
		if err != nil {
			return nil, err
		}
		ociMediatypes := true
		if v, ok := attrs[attrOCIMediatypes]; ok {
			b, err := strconv.ParseBool(v)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse %s", attrOCIMediatypes)
			}
			ociMediatypes = b
		}
		csID := contentStoreIDPrefix + store
		cs, err := getContentStore(ctx, sm, g, csID)
		if err != nil {
			return nil, err
		}
		return remotecache.NewExporter(cs, "", ociMediatypes, *compressionConfig), nil
	}
}

// ResolveCacheImporterFunc for "local" cache importer.
func ResolveCacheImporterFunc(sm *session.Manager) remotecache.ResolveCacheImporterFunc {
	return func(ctx context.Context, g session.Group, attrs map[string]string) (remotecache.Importer, ocispecs.Descriptor, error) {
		dgstStr := attrs[attrDigest]
		if dgstStr == "" {
			return nil, ocispecs.Descriptor{}, errors.New("local cache importer requires explicit digest")
		}
		dgst := digest.Digest(dgstStr)
		store := attrs[attrSrc]
		if store == "" {
			return nil, ocispecs.Descriptor{}, errors.New("local cache importer requires src")
		}
		csID := contentStoreIDPrefix + store
		cs, err := getContentStore(ctx, sm, g, csID)
		if err != nil {
			return nil, ocispecs.Descriptor{}, err
		}
		info, err := cs.Info(ctx, dgst)
		if err != nil {
			return nil, ocispecs.Descriptor{}, err
		}
		desc := ocispecs.Descriptor{
			// MediaType is typically MediaTypeDockerSchema2ManifestList,
			// but we leave it empty until we get correct support for local index.json
			Digest: dgst,
			Size:   info.Size,
		}
		return remotecache.NewImporter(cs), desc, nil
	}
}

func getContentStore(ctx context.Context, sm *session.Manager, g session.Group, storeID string) (content.Store, error) {
	// TODO: to ensure correct session is detected, new api for finding if storeID is supported is needed
	sessionID := g.SessionIterator().NextSession()
	if sessionID == "" {
		return nil, errors.New("local cache exporter/importer requires session")
	}
	timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	caller, err := sm.Get(timeoutCtx, sessionID, false)
	if err != nil {
		return nil, err
	}
	return sessioncontent.NewCallerStore(caller, storeID), nil
}

func attrsToCompression(attrs map[string]string) (*compression.Config, error) {
	compressionType := compression.Default
	if v, ok := attrs[attrLayerCompression]; ok {
		if c := compression.Parse(v); c != compression.UnknownCompression {
			compressionType = c
		}
	}
	compressionConfig := compression.New(compressionType)
	if v, ok := attrs[attrForceCompression]; ok {
		var force bool
		if v == "" {
			force = true
		} else {
			b, err := strconv.ParseBool(v)
			if err != nil {
				return nil, errors.Wrapf(err, "non-bool value %s specified for %s", v, attrForceCompression)
			}
			force = b
		}
		compressionConfig = compressionConfig.SetForce(force)
	}
	if v, ok := attrs[attrCompressionLevel]; ok {
		ii, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return nil, errors.Wrapf(err, "non-integer value %s specified for %s", v, attrCompressionLevel)
		}
		compressionConfig = compressionConfig.SetLevel(int(ii))
	}
	return &compressionConfig, nil
}