mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			158 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package build
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/json"
 | 
						|
	"io"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/containerd/containerd/content"
 | 
						|
	"github.com/containerd/containerd/content/proxy"
 | 
						|
	"github.com/docker/buildx/util/confutil"
 | 
						|
	"github.com/docker/buildx/util/progress"
 | 
						|
	controlapi "github.com/moby/buildkit/api/services/control"
 | 
						|
	"github.com/moby/buildkit/client"
 | 
						|
	provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
 | 
						|
	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"golang.org/x/sync/errgroup"
 | 
						|
)
 | 
						|
 | 
						|
type provenancePredicate struct {
 | 
						|
	Builder *provenanceBuilder `json:"builder,omitempty"`
 | 
						|
	provenancetypes.ProvenancePredicate
 | 
						|
}
 | 
						|
 | 
						|
type provenanceBuilder struct {
 | 
						|
	ID string `json:"id,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
func setRecordProvenance(ctx context.Context, c *client.Client, sr *client.SolveResponse, ref string, pw progress.Writer) error {
 | 
						|
	mode := confutil.MetadataProvenance()
 | 
						|
	if mode == confutil.MetadataProvenanceModeDisabled {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	pw = progress.ResetTime(pw)
 | 
						|
	return progress.Wrap("resolving provenance for metadata file", pw.Write, func(l progress.SubLogger) error {
 | 
						|
		res, err := fetchProvenance(ctx, c, ref, mode)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		for k, v := range res {
 | 
						|
			sr.ExporterResponse[k] = v
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode confutil.MetadataProvenanceMode) (out map[string]string, err error) {
 | 
						|
	cl, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
 | 
						|
		Ref:       ref,
 | 
						|
		EarlyExit: true,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var mu sync.Mutex
 | 
						|
	eg, ctx := errgroup.WithContext(ctx)
 | 
						|
	store := proxy.NewContentStore(c.ContentClient())
 | 
						|
	for {
 | 
						|
		ev, err := cl.Recv()
 | 
						|
		if errors.Is(err, io.EOF) {
 | 
						|
			break
 | 
						|
		} else if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if ev.Record == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if ev.Record.Result != nil {
 | 
						|
			desc := lookupProvenance(ev.Record.Result)
 | 
						|
			if desc == nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			eg.Go(func() error {
 | 
						|
				dt, err := content.ReadBlob(ctx, store, *desc)
 | 
						|
				if err != nil {
 | 
						|
					return errors.Wrapf(err, "failed to load provenance blob from build record")
 | 
						|
				}
 | 
						|
				prv, err := encodeProvenance(dt, mode)
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				mu.Lock()
 | 
						|
				if out == nil {
 | 
						|
					out = make(map[string]string)
 | 
						|
				}
 | 
						|
				out["buildx.build.provenance"] = prv
 | 
						|
				mu.Unlock()
 | 
						|
				return nil
 | 
						|
			})
 | 
						|
		} else if ev.Record.Results != nil {
 | 
						|
			for platform, res := range ev.Record.Results {
 | 
						|
				platform := platform
 | 
						|
				desc := lookupProvenance(res)
 | 
						|
				if desc == nil {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				eg.Go(func() error {
 | 
						|
					dt, err := content.ReadBlob(ctx, store, *desc)
 | 
						|
					if err != nil {
 | 
						|
						return errors.Wrapf(err, "failed to load provenance blob from build record")
 | 
						|
					}
 | 
						|
					prv, err := encodeProvenance(dt, mode)
 | 
						|
					if err != nil {
 | 
						|
						return err
 | 
						|
					}
 | 
						|
					mu.Lock()
 | 
						|
					if out == nil {
 | 
						|
						out = make(map[string]string)
 | 
						|
					}
 | 
						|
					out["buildx.build.provenance/"+platform] = prv
 | 
						|
					mu.Unlock()
 | 
						|
					return nil
 | 
						|
				})
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return out, eg.Wait()
 | 
						|
}
 | 
						|
 | 
						|
func lookupProvenance(res *controlapi.BuildResultInfo) *ocispecs.Descriptor {
 | 
						|
	for _, a := range res.Attestations {
 | 
						|
		if a.MediaType == "application/vnd.in-toto+json" && strings.HasPrefix(a.Annotations["in-toto.io/predicate-type"], "https://slsa.dev/provenance/") {
 | 
						|
			return &ocispecs.Descriptor{
 | 
						|
				Digest:      a.Digest,
 | 
						|
				Size:        a.Size_,
 | 
						|
				MediaType:   a.MediaType,
 | 
						|
				Annotations: a.Annotations,
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func encodeProvenance(dt []byte, mode confutil.MetadataProvenanceMode) (string, error) {
 | 
						|
	var prv provenancePredicate
 | 
						|
	if err := json.Unmarshal(dt, &prv); err != nil {
 | 
						|
		return "", errors.Wrapf(err, "failed to unmarshal provenance")
 | 
						|
	}
 | 
						|
	if prv.Builder != nil && prv.Builder.ID == "" {
 | 
						|
		// reset builder if id is empty
 | 
						|
		prv.Builder = nil
 | 
						|
	}
 | 
						|
	if mode == confutil.MetadataProvenanceModeMin {
 | 
						|
		// reset fields for minimal provenance
 | 
						|
		prv.BuildConfig = nil
 | 
						|
		prv.Metadata = nil
 | 
						|
	}
 | 
						|
	dtprv, err := json.Marshal(prv)
 | 
						|
	if err != nil {
 | 
						|
		return "", errors.Wrapf(err, "failed to marshal provenance")
 | 
						|
	}
 | 
						|
	return base64.StdEncoding.EncodeToString(dtprv), nil
 | 
						|
}
 |