Skip to content

Commit bdef879

Browse files
committed
feat: add deadline and trace context to iamv1.Caller
As a workaround for CEL-Go currently not supporting threading the user context through expression and function evaluation, this patch adds support for the caller to thread request deadline and trace context through to the CEL functions, so that downstream gRPC calls can inherit the request deadline and trace context. This might all be removable if/when CEL attains built-in support for "async functions", as per google/cel-go#368
1 parent d807e57 commit bdef879

File tree

7 files changed

+118
-41
lines changed

7 files changed

+118
-41
lines changed

cmd/iamctl/go.sum

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+
3838
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
3939
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
4040
cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs=
41-
cloud.google.com/go/spanner v1.20.0/go.mod h1:ajR/W06cMHQu7nqQ4irRGplPNoWgejGJlEhlB8xBTKk=
4241
cloud.google.com/go/spanner v1.21.0 h1:NWLJnTTPwKu5OB/3SwL/VkJ9rIpvNPjalWz0p6vywnk=
4342
cloud.google.com/go/spanner v1.21.0/go.mod h1:P1Pl0zyIIdhovaFueBrOjSQ6jKQDfl5bVemE+gdEJog=
4443
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=

cmd/iamctl/internal/examplecmd/exampleservercmd/server.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"google.golang.org/grpc"
2424
"google.golang.org/grpc/codes"
2525
"google.golang.org/grpc/status"
26+
"google.golang.org/protobuf/types/known/timestamppb"
2627
)
2728

2829
func newServer(spannerClient *spanner.Client) (*iamexample.Authorization, error) {
@@ -126,6 +127,9 @@ type googleIdentityTokenCallerResolver struct{}
126127
func (googleIdentityTokenCallerResolver) ResolveCaller(ctx context.Context) (*iamv1.Caller, error) {
127128
const authorizationKey = "authorization"
128129
var result iamv1.Caller
130+
if deadline, ok := ctx.Deadline(); ok {
131+
result.Deadline = timestamppb.New(deadline)
132+
}
129133
token, ok := iamtoken.FromIncomingContext(ctx, authorizationKey)
130134
if !ok {
131135
return &result, nil

iamcaller/chain.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ func (c chainResolver) ResolveCaller(ctx context.Context) (*iamv1.Caller, error)
3636
for key, value := range nextCaller.Metadata {
3737
Add(&result, key, value)
3838
}
39+
// TODO: Remove this when CEL-Go supports async functions with context arguments.
40+
if result.Deadline == nil && nextCaller.Deadline != nil {
41+
result.Deadline = nextCaller.Deadline
42+
}
43+
// TODO: Remove this when CEL-Go supports async functions with context arguments.
44+
if result.TraceContext == "" && nextCaller.TraceContext != "" {
45+
result.TraceContext = nextCaller.TraceContext
46+
}
3947
}
4048
return &result, nil
4149
}

iamcaller/chain_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import (
44
"context"
55
"errors"
66
"testing"
7+
"time"
78

89
iamv1 "go.einride.tech/iam/proto/gen/einride/iam/v1"
910
"google.golang.org/protobuf/testing/protocmp"
11+
"google.golang.org/protobuf/types/known/timestamppb"
1012
"gotest.tools/v3/assert"
1113
)
1214

@@ -83,6 +85,34 @@ func TestChainResolvers(t *testing.T) {
8385
assert.DeepEqual(t, expected, actual, protocmp.Transform())
8486
})
8587

88+
t.Run("deadline", func(t *testing.T) {
89+
expected := &iamv1.Caller{
90+
Deadline: timestamppb.New(time.Unix(1234, 0).UTC()),
91+
Members: []string{"test:bar", "test:foo"},
92+
Metadata: map[string]*iamv1.Caller_Metadata{
93+
"key1": {Members: []string{"test:foo"}},
94+
"key2": {Members: []string{"test:bar"}},
95+
},
96+
}
97+
actual, err := ChainResolvers(constant(expected)).ResolveCaller(context.Background())
98+
assert.NilError(t, err)
99+
assert.DeepEqual(t, expected, actual, protocmp.Transform())
100+
})
101+
102+
t.Run("trace-context", func(t *testing.T) {
103+
expected := &iamv1.Caller{
104+
TraceContext: "mock-trace-context",
105+
Members: []string{"test:bar", "test:foo"},
106+
Metadata: map[string]*iamv1.Caller_Metadata{
107+
"key1": {Members: []string{"test:foo"}},
108+
"key2": {Members: []string{"test:bar"}},
109+
},
110+
}
111+
actual, err := ChainResolvers(constant(expected)).ResolveCaller(context.Background())
112+
assert.NilError(t, err)
113+
assert.DeepEqual(t, expected, actual, protocmp.Transform())
114+
})
115+
86116
t.Run("error", func(t *testing.T) {
87117
actual, err := ChainResolvers(
88118
constant(&iamv1.Caller{

iamexample/caller.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"go.einride.tech/iam/iamcaller"
77
iamv1 "go.einride.tech/iam/proto/gen/einride/iam/v1"
88
"google.golang.org/grpc/metadata"
9+
"google.golang.org/protobuf/types/known/timestamppb"
910
)
1011

1112
// MemberHeader is the gRPC header used by the example server to determine IAM members of the caller.
@@ -23,13 +24,14 @@ type memberHeaderResolver struct{}
2324
// ResolveCaller implements iamcaller.Resolver.
2425
func (m *memberHeaderResolver) ResolveCaller(ctx context.Context) (*iamv1.Caller, error) {
2526
var result iamv1.Caller
26-
md, ok := metadata.FromIncomingContext(ctx)
27-
if !ok {
28-
return &result, nil
27+
if md, ok := metadata.FromIncomingContext(ctx); ok {
28+
iamcaller.Add(&result, MemberHeader, &iamv1.Caller_Metadata{
29+
Members: md.Get(MemberHeader),
30+
})
31+
}
32+
if deadline, ok := ctx.Deadline(); ok {
33+
result.Deadline = timestamppb.New(deadline)
2934
}
30-
iamcaller.Add(&result, MemberHeader, &iamv1.Caller_Metadata{
31-
Members: md.Get(MemberHeader),
32-
})
3335
return &result, nil
3436
}
3537

proto/gen/einride/iam/v1/caller.pb.go

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

proto/src/einride/iam/v1/caller.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ message Caller {
1919
repeated string members = 1;
2020
// Caller identity from gRPC metadata key/value pairs.
2121
map<string, Metadata> metadata = 2;
22+
// Deadline for the caller's request.
23+
// TODO: Remove this when cel-go supports async functions with context threading.
24+
google.protobuf.Timestamp deadline = 3;
25+
// Trace context for the caller's request.
26+
// TODO: Remove this when cel-go supports async functions with context threading.
27+
string trace_context = 4;
2228
// Caller identity for a gRPC metadata key/value pair.
2329
message Metadata {
2430
// The IAM members from the metadata value.

0 commit comments

Comments
 (0)