mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 00:47:48 +08:00

Removes gogo/protobuf from buildx and updates to a version of moby/buildkit where gogo is removed. This also changes how the proto files are generated. This is because newer versions of protobuf are more strict about name conflicts. If two files have the same name (even if they are relative paths) and are used in different protoc commands, they'll conflict in the registry. Since protobuf file generation doesn't work very well with `paths=source_relative`, this removes the `go:generate` expression and just relies on the dockerfile to perform the generation. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
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"
|
|
digest "github.com/opencontainers/go-digest"
|
|
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, mode confutil.MetadataProvenanceMode, pw progress.Writer) error {
|
|
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: digest.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
|
|
}
|