Skip to content

Commit fa98f00

Browse files
authored
Merge pull request #166 from linuxboot/bugfix/propagate_context_values
fix(api): Propagate context values
2 parents eca9e1e + fb97c4d commit fa98f00

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

pkg/api/api.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"time"
1414

1515
"github.com/facebookincubator/go-belt/beltctx"
16+
"github.com/linuxboot/contest/pkg/signaling"
17+
"github.com/linuxboot/contest/pkg/signals"
1618
"github.com/linuxboot/contest/pkg/storage"
1719
"github.com/linuxboot/contest/pkg/storage/limits"
1820
"github.com/linuxboot/contest/pkg/types"
@@ -170,10 +172,9 @@ func (a *API) Start(ctx context.Context, requestor EventRequestor, jobDescriptor
170172
// signals to the job's context. Therefore we use a fresh context
171173
// (without any cancels and signalings) and just passthrough its
172174
// observability belt.
173-
//
174-
// It also loose context values, but there are no any values
175-
// we care about here.
176-
ctx = beltctx.WithField(beltctx.WithBelt(context.Background(), beltctx.Belt(ctx)), "api_method", "start")
175+
ctx = newValuesProxyContext(ctx) // ignore the cancel and deadline signals
176+
ctx, _ = signaling.WithSignal(ctx, signals.Paused) // ignore the pause signal
177+
ctx = beltctx.WithField(ctx, "api_method", "start")
177178

178179
ev := &Event{
179180
Context: ctx,

pkg/api/api_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ package api
77

88
import (
99
"context"
10+
"fmt"
1011
"runtime"
1112
"testing"
1213
"time"
1314

1415
"github.com/linuxboot/contest/pkg/job"
16+
"github.com/linuxboot/contest/pkg/signaling"
17+
"github.com/linuxboot/contest/pkg/signals"
1518

1619
"github.com/facebookincubator/go-belt/tool/logger"
1720

@@ -38,6 +41,83 @@ func (d dummyEventMsg) Requestor() EventRequestor {
3841
return "unit-test"
3942
}
4043

44+
func TestStartContext(t *testing.T) {
45+
t.Run("NoCancel", func(t *testing.T) {
46+
apiInstance, err := New(OptionServerID("unit-test"))
47+
require.NoError(t, err)
48+
49+
ctx, cancelFunc := context.WithCancel(context.Background())
50+
cancelFunc()
51+
52+
go func() {
53+
ev := <-apiInstance.Events
54+
var err error
55+
select {
56+
case <-ev.Context.Done():
57+
err = fmt.Errorf("cancel signal was propagated")
58+
default:
59+
}
60+
ev.RespCh <- &EventResponse{
61+
Err: err,
62+
}
63+
}()
64+
65+
r, err := apiInstance.Start(ctx, "unit-test-requestor", "unit-test-job")
66+
require.NoError(t, err)
67+
require.NoError(t, r.Err)
68+
})
69+
70+
t.Run("NoPause", func(t *testing.T) {
71+
apiInstance, err := New(OptionServerID("unit-test"))
72+
require.NoError(t, err)
73+
74+
ctx, pauseFunc := signaling.WithSignal(context.Background(), signals.Paused)
75+
pauseFunc()
76+
77+
go func() {
78+
ev := <-apiInstance.Events
79+
var err error
80+
select {
81+
case <-ev.Context.Done():
82+
err = fmt.Errorf("pause signal was propagated")
83+
default:
84+
}
85+
ev.RespCh <- &EventResponse{
86+
Err: err,
87+
}
88+
}()
89+
90+
r, err := apiInstance.Start(ctx, "unit-test-requestor", "unit-test-job")
91+
require.NoError(t, err)
92+
require.NoError(t, r.Err)
93+
})
94+
95+
t.Run("HaveValues", func(t *testing.T) {
96+
apiInstance, err := New(OptionServerID("unit-test"))
97+
require.NoError(t, err)
98+
99+
type privateStringType string
100+
var ctxKey = privateStringType("unit-test-key")
101+
102+
ctx := context.WithValue(context.Background(), ctxKey, "unit-test-value")
103+
104+
go func() {
105+
ev := <-apiInstance.Events
106+
var err error
107+
if ctx.Value(ctxKey) != ev.Context.Value(ctxKey) {
108+
err = fmt.Errorf("context value was not propagated correctly: <%v> != <%v>", ev.Context.Value(ctxKey), ctx.Value(ctxKey))
109+
}
110+
ev.RespCh <- &EventResponse{
111+
Err: err,
112+
}
113+
}()
114+
115+
r, err := apiInstance.Start(ctx, "unit-test-requestor", "unit-test-job")
116+
require.NoError(t, err)
117+
require.NoError(t, r.Err)
118+
})
119+
}
120+
41121
func TestEventTimeout(t *testing.T) {
42122
t.Run("timeout", func(t *testing.T) {
43123
apiInstance, err := New(OptionServerID("unit-test"), OptionEventTimeout(time.Nanosecond))

pkg/api/values_proxy_context.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
//
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
package api
7+
8+
import (
9+
"context"
10+
"time"
11+
)
12+
13+
type valuesProxyContext struct {
14+
valueser context.Context
15+
}
16+
17+
var _ context.Context = (*valuesProxyContext)(nil)
18+
19+
// Deadline implements interface context.Context.
20+
func (ctx *valuesProxyContext) Deadline() (time.Time, bool) {
21+
return time.Time{}, false
22+
}
23+
24+
// Done implements interface context.Context.
25+
func (ctx *valuesProxyContext) Done() <-chan struct{} {
26+
return nil
27+
}
28+
29+
// Err implements interface context.Context.
30+
func (ctx *valuesProxyContext) Err() error {
31+
return nil
32+
}
33+
34+
// Value implements interface context.Context.
35+
func (ctx *valuesProxyContext) Value(key any) any {
36+
return ctx.valueser.Value(key)
37+
}
38+
39+
// newValuesProxyContext returns a context without cancellation/deadline signals,
40+
// but with all the values kept as is.
41+
func newValuesProxyContext(ctx context.Context) context.Context {
42+
ctx = &valuesProxyContext{
43+
valueser: ctx,
44+
}
45+
return ctx
46+
}

0 commit comments

Comments
 (0)