Skip to content

Commit

Permalink
v3: Analyze layer content in parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
KeyboardNerd committed Mar 20, 2019
1 parent 2c7838e commit 88f5069
Showing 1 changed file with 41 additions and 23 deletions.
64 changes: 41 additions & 23 deletions api/v3/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
package v3

import (
"sync"

"golang.org/x/net/context"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

Expand Down Expand Up @@ -83,32 +86,47 @@ func (s *AncestryServer) PostAncestry(ctx context.Context, req *pb.PostAncestryR
}

builder := clair.NewAncestryBuilder(clair.EnabledDetectors())
for _, layer := range req.Layers {
if layer == nil {
err := status.Error(codes.InvalidArgument, "ancestry layer is invalid")
return nil, err
}

if layer.GetHash() == "" {
return nil, status.Error(codes.InvalidArgument, "ancestry layer hash should not be empty")
}

if layer.GetPath() == "" {
return nil, status.Error(codes.InvalidArgument, "ancestry layer path should not be empty")
layerMap := map[string]*database.Layer{}
layerMapLock := sync.RWMutex{}
g, analyzerCtx := errgroup.WithContext(ctx)
for i := range req.Layers {
layer := req.Layers[i]
if _, ok := layerMap[layer.Hash]; !ok {
layerMap[layer.Hash] = nil
if layer == nil {
err := status.Error(codes.InvalidArgument, "ancestry layer is invalid")
return nil, err
}

if layer.GetHash() == "" {
return nil, status.Error(codes.InvalidArgument, "ancestry layer hash should not be empty")
}

if layer.GetPath() == "" {
return nil, status.Error(codes.InvalidArgument, "ancestry layer path should not be empty")
}

g.Go(func() error {
clairLayer, err := clair.AnalyzeLayer(analyzerCtx, s.Store, layer.Hash, req.Format, layer.Path, layer.Headers)
if err != nil {
return err
}

layerMapLock.Lock()
layerMap[layer.Hash] = clairLayer
layerMapLock.Unlock()

return nil
})
}
}

// TODO(sidac): make AnalyzeLayer to be async to ensure
// non-blocking downloads.
// We'll need to deal with two layers post by the same or different
// requests that may have the same hash. In that case, since every
// layer/feature/namespace is unique in the database, it may introduce
// deadlock.
clairLayer, err := clair.AnalyzeLayer(ctx, s.Store, layer.Hash, req.Format, layer.Path, layer.Headers)
if err != nil {
return nil, newRPCErrorWithClairError(codes.Internal, err)
}
if err = g.Wait(); err != nil {
return nil, newRPCErrorWithClairError(codes.Internal, err)
}

builder.AddLeafLayer(clairLayer)
for _, layer := range req.Layers {
builder.AddLeafLayer(layerMap[layer.Hash])
}

if err := clair.SaveAncestry(s.Store, builder.Ancestry(req.AncestryName)); err != nil {
Expand Down

0 comments on commit 88f5069

Please sign in to comment.