Skip to content

Commit ba6f3df

Browse files
authored
Merge pull request moby#5109 from tonistiigi/history-finalizer
solver: allow finalizing history record traces
2 parents f6c85ab + 3139b16 commit ba6f3df

File tree

6 files changed

+322
-160
lines changed

6 files changed

+322
-160
lines changed

api/services/control/control.pb.go

Lines changed: 189 additions & 147 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/services/control/control.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ message UpdateBuildHistoryRequest {
220220
string Ref = 1;
221221
bool Pinned = 2;
222222
bool Delete = 3;
223+
bool Finalize = 4;
223224
}
224225

225226
message UpdateBuildHistoryResponse {}

control/control.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,18 +283,23 @@ func (c *Controller) ListenBuildHistory(req *controlapi.BuildHistoryRequest, srv
283283
}
284284

285285
func (c *Controller) UpdateBuildHistory(ctx context.Context, req *controlapi.UpdateBuildHistoryRequest) (*controlapi.UpdateBuildHistoryResponse, error) {
286-
if !req.Delete {
287-
err := c.history.UpdateRef(ctx, req.Ref, func(r *controlapi.BuildHistoryRecord) error {
288-
if req.Pinned == r.Pinned {
289-
return nil
290-
}
291-
r.Pinned = req.Pinned
292-
return nil
293-
})
286+
if req.Delete {
287+
err := c.history.Delete(ctx, req.Ref)
294288
return &controlapi.UpdateBuildHistoryResponse{}, err
295289
}
296290

297-
err := c.history.Delete(ctx, req.Ref)
291+
if req.Finalize {
292+
err := c.history.Finalize(ctx, req.Ref)
293+
return &controlapi.UpdateBuildHistoryResponse{}, err
294+
}
295+
296+
err := c.history.UpdateRef(ctx, req.Ref, func(r *controlapi.BuildHistoryRecord) error {
297+
if req.Pinned == r.Pinned {
298+
return nil
299+
}
300+
r.Pinned = req.Pinned
301+
return nil
302+
})
298303
return &controlapi.UpdateBuildHistoryResponse{}, err
299304
}
300305

frontend/dockerfile/dockerfile_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ var allTests = integration.TestFuncs(
194194
testSourcePolicyWithNamedContext,
195195
testInvalidJSONCommands,
196196
testHistoryError,
197+
testHistoryFinalizeTrace,
197198
)
198199

199200
// Tests that depend on the `security.*` entitlements
@@ -7521,6 +7522,66 @@ COPY notexist /foo
75217522
}
75227523
}
75237524

7525+
func testHistoryFinalizeTrace(t *testing.T, sb integration.Sandbox) {
7526+
integration.SkipOnPlatform(t, "windows")
7527+
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush)
7528+
ctx := sb.Context()
7529+
7530+
c, err := client.New(ctx, sb.Address())
7531+
require.NoError(t, err)
7532+
defer c.Close()
7533+
7534+
f := getFrontend(t, sb)
7535+
7536+
dockerfile := []byte(`
7537+
FROM scratch
7538+
COPY Dockerfile /foo
7539+
`)
7540+
dir := integration.Tmpdir(
7541+
t,
7542+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
7543+
)
7544+
7545+
ref := identity.NewID()
7546+
7547+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
7548+
Ref: ref,
7549+
LocalMounts: map[string]fsutil.FS{
7550+
dockerui.DefaultLocalNameDockerfile: dir,
7551+
dockerui.DefaultLocalNameContext: dir,
7552+
},
7553+
}, nil)
7554+
require.NoError(t, err)
7555+
7556+
_, err = c.ControlClient().UpdateBuildHistory(sb.Context(), &controlapi.UpdateBuildHistoryRequest{
7557+
Ref: ref,
7558+
Finalize: true,
7559+
})
7560+
require.NoError(t, err)
7561+
7562+
cl, err := c.ControlClient().ListenBuildHistory(sb.Context(), &controlapi.BuildHistoryRequest{
7563+
EarlyExit: true,
7564+
Ref: ref,
7565+
})
7566+
require.NoError(t, err)
7567+
7568+
got := false
7569+
for {
7570+
resp, err := cl.Recv()
7571+
if err == io.EOF {
7572+
require.Equal(t, true, got)
7573+
break
7574+
}
7575+
require.NoError(t, err)
7576+
got = true
7577+
7578+
trace := resp.Record.Trace
7579+
require.NotEmpty(t, trace)
7580+
7581+
require.NotEmpty(t, trace.Digest)
7582+
}
7583+
}
7584+
75247585
func runShell(dir string, cmds ...string) error {
75257586
for _, args := range cmds {
75267587
var cmd *exec.Cmd

solver/llbsolver/history.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,20 @@ type HistoryQueue struct {
5252
opt HistoryQueueOpt
5353
ps *pubsub[*controlapi.BuildHistoryEvent]
5454
active map[string]*controlapi.BuildHistoryRecord
55+
finalizers map[string]*finalizer
5556
refs map[string]int
5657
deleted map[string]struct{}
5758
hContentStore *containerdsnapshot.Store
5859
hLeaseManager *leaseutil.Manager
5960
}
6061

62+
// finalizer controls completion of saving traces for a
63+
// record and making it immutable
64+
type finalizer struct {
65+
trigger func()
66+
done chan struct{}
67+
}
68+
6169
type StatusImportResult struct {
6270
Descriptor ocispecs.Descriptor
6371
NumCachedSteps int
@@ -77,9 +85,10 @@ func NewHistoryQueue(opt HistoryQueueOpt) (*HistoryQueue, error) {
7785
ps: &pubsub[*controlapi.BuildHistoryEvent]{
7886
m: map[*channel[*controlapi.BuildHistoryEvent]]struct{}{},
7987
},
80-
active: map[string]*controlapi.BuildHistoryRecord{},
81-
refs: map[string]int{},
82-
deleted: map[string]struct{}{},
88+
active: map[string]*controlapi.BuildHistoryRecord{},
89+
refs: map[string]int{},
90+
deleted: map[string]struct{}{},
91+
finalizers: map[string]*finalizer{},
8392
}
8493

8594
ns := h.opt.ContentStore.Namespace()
@@ -570,6 +579,40 @@ func (h *HistoryQueue) update(ctx context.Context, rec controlapi.BuildHistoryRe
570579
})
571580
}
572581

582+
func (h *HistoryQueue) AcquireFinalizer(ref string) (<-chan struct{}, func()) {
583+
h.mu.Lock()
584+
defer h.mu.Unlock()
585+
trigger := make(chan struct{})
586+
f := &finalizer{
587+
trigger: sync.OnceFunc(func() {
588+
close(trigger)
589+
}),
590+
done: make(chan struct{}),
591+
}
592+
h.finalizers[ref] = f
593+
go func() {
594+
<-f.done
595+
h.mu.Lock()
596+
delete(h.finalizers, ref)
597+
h.mu.Unlock()
598+
}()
599+
return trigger, sync.OnceFunc(func() {
600+
close(f.done)
601+
})
602+
}
603+
604+
func (h *HistoryQueue) Finalize(ctx context.Context, ref string) error {
605+
h.mu.Lock()
606+
f, ok := h.finalizers[ref]
607+
h.mu.Unlock()
608+
if !ok {
609+
return nil
610+
}
611+
f.trigger()
612+
<-f.done
613+
return nil
614+
}
615+
573616
func (h *HistoryQueue) Update(ctx context.Context, e *controlapi.BuildHistoryEvent) error {
574617
h.init()
575618
h.mu.Lock()

solver/llbsolver/solver.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend
385385
releasers = append(releasers, release)
386386
rec.Error = status
387387
}
388+
389+
ready, done := s.history.AcquireFinalizer(rec.Ref)
390+
388391
if err1 := s.history.Update(ctx, &controlapi.BuildHistoryEvent{
389392
Type: controlapi.BuildHistoryEventType_COMPLETE,
390393
Record: rec,
@@ -396,10 +399,17 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend
396399

397400
if stopTrace == nil {
398401
bklog.G(ctx).Warn("no trace recorder found, skipping")
402+
done()
399403
return err
400404
}
401405
go func() {
402-
time.Sleep(3 * time.Second)
406+
defer done()
407+
408+
// if there is no finalizer request then stop tracing after 3 seconds
409+
select {
410+
case <-time.After(3 * time.Second):
411+
case <-ready:
412+
}
403413
spans := stopTrace()
404414

405415
if len(spans) == 0 {

0 commit comments

Comments
 (0)