Skip to content

Commit 881151a

Browse files
adrienthebocsweichel
authored andcommitted
stub existing packages
1 parent 2bc371a commit 881151a

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

cmd/root.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ func getRemoteCache() leeway.RemoteCache {
173173
return leeway.GSUtilRemoteCache{
174174
BucketName: remoteCacheBucket,
175175
}
176+
case "AWS":
177+
rc, err := leeway.NewS3RemoteCache(remoteCacheBucket, nil)
178+
if err != nil {
179+
log.Warnf("S3 cache initialization failed; remote cache will be disabled: %s", err)
180+
return leeway.NoRemoteCache{}
181+
} else {
182+
return rc
183+
}
176184
default:
177185
return leeway.GSUtilRemoteCache{
178186
BucketName: remoteCacheBucket,

go.mod

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ require (
3030
)
3131

3232
require (
33+
github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect
34+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
35+
github.com/aws/aws-sdk-go-v2/config v1.18.8 // indirect
36+
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 // indirect
37+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
38+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
39+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
40+
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
41+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18 // indirect
42+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
43+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 // indirect
44+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
45+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 // indirect
46+
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0 // indirect
47+
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect
48+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect
49+
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect
50+
github.com/aws/smithy-go v1.13.5 // indirect
3351
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
3452
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
3553
github.com/davecgh/go-spew v1.1.1 // indirect

go.sum

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,40 @@
11
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2+
github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
3+
github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
4+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
5+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
6+
github.com/aws/aws-sdk-go-v2/config v1.18.8 h1:lDpy0WM8AHsywOnVrOHaSMfpaiV2igOw8D7svkFkXVA=
7+
github.com/aws/aws-sdk-go-v2/config v1.18.8/go.mod h1:5XCmmyutmzzgkpk/6NYTjeWb6lgo9N170m1j6pQkIBs=
8+
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 h1:vTrwTvv5qAwjWIGhZDSBH/oQHuIQjGmD232k01FUh6A=
9+
github.com/aws/aws-sdk-go-v2/credentials v1.13.8/go.mod h1:lVa4OHbvgjVot4gmh1uouF1ubgexSCN92P6CJQpT0t8=
10+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU=
11+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg=
12+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
13+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
14+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
15+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
16+
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ=
17+
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c=
18+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18 h1:H/mF2LNWwX00lD6FlYfKpLLZgUW7oIzCBkig78x4Xok=
19+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18/go.mod h1:T2Ku+STrYQ1zIkL1wMvj8P3wWQaaCMKNdz70MT2FLfE=
20+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
21+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
22+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 h1:kv5vRAl00tozRxSnI0IszPWGXsJOyA7hmEUHFYqsyvw=
23+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22/go.mod h1:Od+GU5+Yx41gryN/ZGZzAJMZ9R1yn6lgA0fD5Lo5SkQ=
24+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM=
25+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k=
26+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 h1:vY5siRXvW5TrOKm2qKEf9tliBfdLxdfy0i02LOcmqUo=
27+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21/go.mod h1:WZvNXT1XuH8dnJM0HvOlvk+RNn7NbAPvA/ACO0QarSc=
28+
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0 h1:wddsyuESfviaiXk3w9N6/4iRwTg/a3gktjODY6jYQBo=
29+
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0/go.mod h1:L2l2/q76teehcW7YEsgsDjqdsDTERJeX3nOMIFlgGUE=
30+
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 h1:/2gzjhQowRLarkkBOGPXSRnb8sQ2RVsjdG1C/UliK/c=
31+
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A=
32+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 h1:Jfly6mRxk2ZOSlbCvZfKNS7TukSx1mIzhSsqZ/IGSZI=
33+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8=
34+
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 h1:kOO++CYo50RcTFISESluhWEi5Prhg+gaSs4whWabiZU=
35+
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I=
36+
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
37+
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
238
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
339
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
440
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
@@ -41,6 +77,7 @@ github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
4177
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
4278
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
4379
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
80+
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
4481
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
4582
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
4683
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
@@ -51,6 +88,8 @@ github.com/in-toto/in-toto-golang v0.3.3 h1:tkkEBU5i09UEeWKnrp6Rq4fXKAfpVXYMLRO5
5188
github.com/in-toto/in-toto-golang v0.3.3/go.mod h1:dbXecHGZSqRubmm5TXtvDSZT5JyaKD7ebVTiC2aMLWY=
5289
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
5390
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
91+
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
92+
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
5493
github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
5594
github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
5695
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -152,6 +191,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
152191
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
153192
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
154193
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
194+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
155195
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
156196
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
157197
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/leeway/cache.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@ package leeway
33
import (
44
"bufio"
55
"bytes"
6+
"context"
7+
"errors"
68
"fmt"
79
"io"
810
"os"
911
"os/exec"
1012
"path/filepath"
1113
"strings"
14+
"sync"
1215
"syscall"
1316

17+
"github.com/aws/aws-sdk-go-v2/aws"
18+
"github.com/aws/aws-sdk-go-v2/config"
19+
"github.com/aws/aws-sdk-go-v2/service/s3"
20+
"github.com/aws/aws-sdk-go-v2/service/s3/types"
21+
"github.com/aws/aws-sdk-go-v2/service/sts"
1422
log "github.com/sirupsen/logrus"
1523
"golang.org/x/xerrors"
1624
)
@@ -231,3 +239,150 @@ func gsutilTransfer(target string, files []string) error {
231239
}
232240
return nil
233241
}
242+
243+
// S3RemoteCache uses the AWS Go SDK to implement a remote cache
244+
type S3RemoteCache struct {
245+
BucketName string
246+
s3Config *aws.Config
247+
s3Client *s3.Client
248+
}
249+
250+
func NewS3RemoteCache(bucketName string, cfg *aws.Config) (*S3RemoteCache, error) {
251+
if cfg == nil {
252+
v, err := config.LoadDefaultConfig(context.TODO())
253+
cfg = &v
254+
if err != nil {
255+
return nil, fmt.Errorf("cannot load s3 config: %s", err)
256+
}
257+
}
258+
s3Client := s3.NewFromConfig(*cfg)
259+
260+
log.DebugFn(func() []interface{} {
261+
stsClient := sts.NewFromConfig(*cfg)
262+
identity, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{})
263+
if err != nil {
264+
log.Warnf("Cannot get AWS caller identity: %s", err)
265+
return nil
266+
}
267+
268+
log.WithFields(log.Fields{
269+
"Account": aws.ToString(identity.Account),
270+
"Arn": aws.ToString(identity.Arn),
271+
"Region": cfg.Region,
272+
}).Debug("Loaded AWS account")
273+
274+
return nil
275+
})
276+
277+
return &S3RemoteCache{bucketName, cfg, s3Client}, nil
278+
}
279+
280+
// ExistingPackages returns existing cached build artifacts in the remote cache
281+
func (rs *S3RemoteCache) ExistingPackages(pkgs []*Package) (map[*Package]struct{}, error) {
282+
packagesToKeys := make(map[*Package]string)
283+
for _, p := range pkgs {
284+
version, err := p.Version()
285+
if err != nil {
286+
log.WithField("package", p.FullName()).Debug("Failed to get version for package. Will not check remote cache for package.")
287+
continue
288+
}
289+
290+
packagesToKeys[p] = fmt.Sprintf("%s.tar.gz", version)
291+
}
292+
293+
if len(packagesToKeys) == 0 {
294+
return map[*Package]struct{}{}, nil
295+
}
296+
log.Debugf("Checking if %d packages exist in the remote cache using s3", len(packagesToKeys))
297+
298+
ch := make(chan *Package, len(packagesToKeys))
299+
defer close(ch)
300+
301+
existingPackages := make(map[*Package]struct{})
302+
wg := sync.WaitGroup{}
303+
304+
for pkg, key := range packagesToKeys {
305+
go func(pkg *Package, key string) {
306+
defer wg.Done()
307+
308+
stat, _ := rs.hasObject(context.TODO(), key)
309+
// TODO error handling
310+
if stat {
311+
ch <- pkg
312+
}
313+
}(pkg, key)
314+
315+
wg.Add(1)
316+
}
317+
wg.Wait()
318+
319+
return existingPackages, nil
320+
}
321+
322+
// Download makes a best-effort attempt at downloading previously cached build artifacts for the given packages
323+
// in their current version. A cache miss (i.e. a build artifact not being available) does not constitute an
324+
// error. Get should try and download as many artifacts as possible.
325+
func (s3 *S3RemoteCache) Download(dst Cache, pkgs []*Package) error {
326+
panic("not implemented") // TODO: Implement
327+
}
328+
329+
// Upload makes a best effort to upload the build arfitacts to a remote cache. If uploading an artifact fails, that
330+
// does not constitute an error.
331+
func (s3 *S3RemoteCache) Upload(src Cache, pkgs []*Package) error {
332+
panic("not implemented") // TODO: Implement
333+
}
334+
335+
func (rs *S3RemoteCache) hasBucket(ctx context.Context) (bool, error) {
336+
cfg := *rs.s3Config
337+
fields := log.Fields{
338+
"bucket": rs.BucketName,
339+
"region": cfg.Region,
340+
}
341+
log.WithFields(fields).Debugf("Checking s3 for cache bucket")
342+
343+
_, err := rs.s3Client.HeadBucket(ctx, &s3.HeadBucketInput{
344+
Bucket: aws.String(rs.BucketName),
345+
})
346+
347+
if err != nil {
348+
var nsk *types.NoSuchBucket
349+
if errors.As(err, &nsk) {
350+
return false, nil
351+
}
352+
log.WithFields(fields).Errorf("Failed to get bucket: %s", err)
353+
return false, err
354+
}
355+
return true, nil
356+
}
357+
358+
func (rs *S3RemoteCache) hasObject(ctx context.Context, key string) (bool, error) {
359+
cfg := *rs.s3Config
360+
fields := log.Fields{
361+
"key": key,
362+
"bucket": rs.BucketName,
363+
"region": cfg.Region,
364+
}
365+
log.WithFields(fields).Debugf("Checking s3 for cached package")
366+
367+
_, err := rs.s3Client.GetObject(ctx, &s3.GetObjectInput{
368+
Bucket: aws.String(rs.BucketName),
369+
Key: aws.String(key),
370+
Range: aws.String("bytes=0-0"),
371+
})
372+
373+
if err != nil {
374+
var nsk *types.NoSuchKey
375+
if errors.As(err, &nsk) {
376+
return false, nil
377+
}
378+
379+
// We've received an error that's not a simple missing key error. Collect more information
380+
_, _ = rs.hasBucket(ctx)
381+
382+
log.WithFields(fields).Warnf("S3 GetObject failed: %s", err)
383+
return false, err
384+
}
385+
386+
// XXX
387+
return true, nil
388+
}

0 commit comments

Comments
 (0)