history: make sure build record is finalized before exporting
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
@@ -20,10 +20,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type exportOptions struct {
|
type exportOptions struct {
|
||||||
builder string
|
builder string
|
||||||
refs []string
|
refs []string
|
||||||
output string
|
output string
|
||||||
all bool
|
all bool
|
||||||
|
finalize bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExport(ctx context.Context, dockerCli command.Cli, opts exportOptions) error {
|
func runExport(ctx context.Context, dockerCli command.Cli, opts exportOptions) error {
|
||||||
@@ -62,6 +63,26 @@ func runExport(ctx context.Context, dockerCli command.Cli, opts exportOptions) e
|
|||||||
return errors.Errorf("no record found for ref %q", ref)
|
return errors.Errorf("no record found for ref %q", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.finalize {
|
||||||
|
var finalized bool
|
||||||
|
for _, rec := range recs {
|
||||||
|
if rec.Trace == nil {
|
||||||
|
finalized = true
|
||||||
|
if err := finalizeRecord(ctx, rec.Ref, nodes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if finalized {
|
||||||
|
recs, err = queryRecords(ctx, ref, nodes, &queryOptions{
|
||||||
|
CompletedOnly: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ref == "" {
|
if ref == "" {
|
||||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
slices.SortFunc(recs, func(a, b historyRecord) int {
|
||||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
||||||
@@ -154,7 +175,8 @@ func exportCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
|||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.StringVarP(&options.output, "output", "o", "", "Output file path")
|
flags.StringVarP(&options.output, "output", "o", "", "Output file path")
|
||||||
flags.BoolVar(&options.all, "all", false, "Export all records for the builder")
|
flags.BoolVar(&options.all, "all", false, "Export all build records for the builder")
|
||||||
|
flags.BoolVar(&options.finalize, "finalize", false, "Ensure build records are finalized before exporting")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/docker/buildx/util/otelutil"
|
"github.com/docker/buildx/util/otelutil"
|
||||||
"github.com/docker/buildx/util/otelutil/jaeger"
|
"github.com/docker/buildx/util/otelutil/jaeger"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
controlapi "github.com/moby/buildkit/api/services/control"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/browser"
|
"github.com/pkg/browser"
|
||||||
@@ -57,14 +56,7 @@ func loadTrace(ctx context.Context, ref string, nodes []builder.Node) (string, [
|
|||||||
// build is complete but no trace yet. try to finalize the trace
|
// build is complete but no trace yet. try to finalize the trace
|
||||||
time.Sleep(1 * time.Second) // give some extra time for last parts of trace to be written
|
time.Sleep(1 * time.Second) // give some extra time for last parts of trace to be written
|
||||||
|
|
||||||
c, err := rec.node.Driver.Client(ctx)
|
err := finalizeRecord(ctx, rec.Ref, []builder.Node{*rec.node})
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
|
|
||||||
Ref: rec.Ref,
|
|
||||||
Finalize: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,6 +248,28 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func finalizeRecord(ctx context.Context, ref string, nodes []builder.Node) error {
|
||||||
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
|
for _, node := range nodes {
|
||||||
|
node := node
|
||||||
|
eg.Go(func() error {
|
||||||
|
if node.Driver == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c, err := node.Driver.Client(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
|
||||||
|
Ref: ref,
|
||||||
|
Finalize: true,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return eg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func formatDuration(d time.Duration) string {
|
func formatDuration(d time.Duration) string {
|
||||||
if d < time.Minute {
|
if d < time.Minute {
|
||||||
return fmt.Sprintf("%.1fs", d.Seconds())
|
return fmt.Sprintf("%.1fs", d.Seconds())
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ Export build records into Docker Desktop bundle
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:---------------------------------------|:---------|:--------|:-----------------------------------------|
|
|:---------------------------------------|:---------|:--------|:----------------------------------------------------|
|
||||||
| [`--all`](#all) | `bool` | | Export all records for the builder |
|
| [`--all`](#all) | `bool` | | Export all build records for the builder |
|
||||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||||
| [`-D`](#debug), [`--debug`](#debug) | `bool` | | Enable debug logging |
|
| [`-D`](#debug), [`--debug`](#debug) | `bool` | | Enable debug logging |
|
||||||
| [`-o`](#output), [`--output`](#output) | `string` | | Output file path |
|
| [`--finalize`](#finalize) | `bool` | | Ensure build records are finalized before exporting |
|
||||||
|
| [`-o`](#output), [`--output`](#output) | `string` | | Output file path |
|
||||||
|
|
||||||
|
|
||||||
<!---MARKER_GEN_END-->
|
<!---MARKER_GEN_END-->
|
||||||
@@ -49,6 +50,16 @@ docker buildx history export --builder builder0 ^1 -o builder0-build.dockerbuild
|
|||||||
docker buildx history export --debug qu2gsuo8ejqrwdfii23xkkckt -o debug-build.dockerbuild
|
docker buildx history export --debug qu2gsuo8ejqrwdfii23xkkckt -o debug-build.dockerbuild
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <a name="finalize"></a> Ensure build records are finalized before exporting (--finalize)
|
||||||
|
|
||||||
|
Clients can report their own traces concurrently, and not all traces may be
|
||||||
|
saved yet by the time of the export. Use the `--finalize` flag to ensure all
|
||||||
|
traces are finalized before exporting.
|
||||||
|
|
||||||
|
```console
|
||||||
|
docker buildx history export --finalize qu2gsuo8ejqrwdfii23xkkckt -o finalized-build.dockerbuild
|
||||||
|
```
|
||||||
|
|
||||||
### <a name="output"></a> Export a single build to a custom file (--output)
|
### <a name="output"></a> Export a single build to a custom file (--output)
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|||||||
Reference in New Issue
Block a user