Skip to content

Commit 2191665

Browse files
committed
Merge main
2 parents 25b351c + d0a2fcb commit 2191665

File tree

13 files changed

+453
-302
lines changed

13 files changed

+453
-302
lines changed

doc/index.html

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,10 @@ <h2>Table of Contents</h2>
14721472
<a href="#metalstack.api.v2.SwitchQuery"><span class="badge">M</span>SwitchQuery</a>
14731473
</li>
14741474

1475+
<li>
1476+
<a href="#metalstack.api.v2.SwitchSync"><span class="badge">M</span>SwitchSync</a>
1477+
</li>
1478+
14751479

14761480
<li>
14771481
<a href="#metalstack.api.v2.BGPState"><span class="badge">E</span>BGPState</a>
@@ -2229,10 +2233,6 @@ <h2>Table of Contents</h2>
22292233
<a href="#metalstack.infra.v2.SwitchServiceRegisterResponse"><span class="badge">M</span>SwitchServiceRegisterResponse</a>
22302234
</li>
22312235

2232-
<li>
2233-
<a href="#metalstack.infra.v2.SwitchSync"><span class="badge">M</span>SwitchSync</a>
2234-
</li>
2235-
22362236

22372237

22382238

@@ -12498,6 +12498,20 @@ <h3 id="metalstack.api.v2.Switch">Switch</h3>
1249812498
<td><p>MachineConnections map machines to the nics they are connected to. </p></td>
1249912499
</tr>
1250012500

12501+
<tr>
12502+
<td>last_sync</td>
12503+
<td><a href="#metalstack.api.v2.SwitchSync">SwitchSync</a></td>
12504+
<td></td>
12505+
<td><p>LastSync contains information about the last heartbeat of the switch. </p></td>
12506+
</tr>
12507+
12508+
<tr>
12509+
<td>last_sync_error</td>
12510+
<td><a href="#metalstack.api.v2.SwitchSync">SwitchSync</a></td>
12511+
<td></td>
12512+
<td><p>LastSyncError contains information about the last unsuccessful heartbeat of the switch. </p></td>
12513+
</tr>
12514+
1250112515
</tbody>
1250212516
</table>
1250312517

@@ -12751,6 +12765,44 @@ <h3 id="metalstack.api.v2.SwitchQuery">SwitchQuery</h3>
1275112765

1275212766

1275312767

12768+
<h3 id="metalstack.api.v2.SwitchSync">SwitchSync</h3>
12769+
<p>SwitchSync summarizes information about a switch sync.</p>
12770+
12771+
12772+
<table class="field-table">
12773+
<thead>
12774+
<tr><td>Field</td><td>Type</td><td>Label</td><td>Description</td></tr>
12775+
</thead>
12776+
<tbody>
12777+
12778+
<tr>
12779+
<td>time</td>
12780+
<td><a href="#google.protobuf.Timestamp">google.protobuf.Timestamp</a></td>
12781+
<td></td>
12782+
<td><p>Time of the sync. </p></td>
12783+
</tr>
12784+
12785+
<tr>
12786+
<td>duration</td>
12787+
<td><a href="#google.protobuf.Duration">google.protobuf.Duration</a></td>
12788+
<td></td>
12789+
<td><p>Duration of the sync. </p></td>
12790+
</tr>
12791+
12792+
<tr>
12793+
<td>error</td>
12794+
<td><a href="#string">string</a></td>
12795+
<td>optional</td>
12796+
<td><p>Error if any occurred. </p></td>
12797+
</tr>
12798+
12799+
</tbody>
12800+
</table>
12801+
12802+
12803+
12804+
12805+
1275412806

1275512807

1275612808
<h3 id="metalstack.api.v2.BGPState">BGPState</h3>
@@ -17695,14 +17747,14 @@ <h3 id="metalstack.infra.v2.SwitchServiceHeartbeatResponse">SwitchServiceHeartbe
1769517747

1769617748
<tr>
1769717749
<td>last_sync</td>
17698-
<td><a href="#metalstack.infra.v2.SwitchSync">SwitchSync</a></td>
17750+
<td><a href="#metalstack.api.v2.SwitchSync">metalstack.api.v2.SwitchSync</a></td>
1769917751
<td></td>
1770017752
<td><p>LastSync holds information about the last sync. </p></td>
1770117753
</tr>
1770217754

1770317755
<tr>
1770417756
<td>last_sync_error</td>
17705-
<td><a href="#metalstack.infra.v2.SwitchSync">SwitchSync</a></td>
17757+
<td><a href="#metalstack.api.v2.SwitchSync">metalstack.api.v2.SwitchSync</a></td>
1770617758
<td></td>
1770717759
<td><p>LastSyncError holds information about the last erroneous sync. </p></td>
1770817760
</tr>
@@ -17762,44 +17814,6 @@ <h3 id="metalstack.infra.v2.SwitchServiceRegisterResponse">SwitchServiceRegister
1776217814

1776317815

1776417816

17765-
<h3 id="metalstack.infra.v2.SwitchSync">SwitchSync</h3>
17766-
<p>SwitchSync summarizes information about a switch sync.</p>
17767-
17768-
17769-
<table class="field-table">
17770-
<thead>
17771-
<tr><td>Field</td><td>Type</td><td>Label</td><td>Description</td></tr>
17772-
</thead>
17773-
<tbody>
17774-
17775-
<tr>
17776-
<td>time</td>
17777-
<td><a href="#google.protobuf.Timestamp">google.protobuf.Timestamp</a></td>
17778-
<td></td>
17779-
<td><p>Time of the sync. </p></td>
17780-
</tr>
17781-
17782-
<tr>
17783-
<td>duration</td>
17784-
<td><a href="#google.protobuf.Duration">google.protobuf.Duration</a></td>
17785-
<td></td>
17786-
<td><p>Duration of the sync. </p></td>
17787-
</tr>
17788-
17789-
<tr>
17790-
<td>error</td>
17791-
<td><a href="#string">string</a></td>
17792-
<td>optional</td>
17793-
<td><p>Error if any occurred. </p></td>
17794-
</tr>
17795-
17796-
</tbody>
17797-
</table>
17798-
17799-
17800-
17801-
17802-
1780317817

1780417818

1780517819

generate/go_client.tpl

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
package client
33

44
import (
5-
"context"
65
"sync"
76

87
"connectrpc.com/connect"
@@ -53,33 +52,17 @@ func New(config *DialConfig) (Client, error) {
5352
interceptors: []connect.Interceptor{},
5453
}
5554

56-
authInterceptor := connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
57-
return connect.UnaryFunc(func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) {
58-
request.Header().Add("Authorization", "Bearer "+config.Token)
59-
return next(ctx, request)
60-
})
61-
})
62-
63-
loggingInterceptor := connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
64-
return connect.UnaryFunc(func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) {
65-
config.Log.Debug("intercept", "request procedure", request.Spec().Procedure, "body", request.Any())
66-
response, err := next(ctx, request)
67-
if err != nil {
68-
return nil, err
69-
}
70-
config.Log.Debug("intercept", "request procedure", request.Spec().Procedure, "response", response.Any())
71-
return response, err
72-
})
73-
})
74-
7555
if config.Token != "" {
56+
authInterceptor := &authInterceptor{config: config}
7657
c.interceptors = append(c.interceptors, authInterceptor)
7758
}
7859
if config.Log != nil {
60+
loggingInterceptor := &loggingInterceptor{config: config}
7961
c.interceptors = append(c.interceptors, loggingInterceptor)
8062
}
8163
c.interceptors = append(c.interceptors, config.Interceptors...)
8264

65+
// TODO convert to interceptor
8366
go c.startTokenRenewal()
8467

8568
return c, nil

go/client/client-interceptors.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package client
2+
3+
import (
4+
"context"
5+
6+
"connectrpc.com/connect"
7+
)
8+
9+
// authinterceptor adds the required auth headers
10+
type authInterceptor struct {
11+
config *DialConfig
12+
}
13+
14+
func (i *authInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
15+
return connect.UnaryFunc(func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) {
16+
request.Header().Add("Authorization", "Bearer "+i.config.Token)
17+
return next(ctx, request)
18+
})
19+
}
20+
21+
func (i *authInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
22+
return func(ctx context.Context, spec connect.Spec) connect.StreamingClientConn {
23+
return &streamingInterceptorConn{
24+
StreamingClientConn: next(ctx, spec),
25+
token: i.config.Token,
26+
}
27+
}
28+
}
29+
30+
func (i *authInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
31+
return next
32+
}
33+
34+
type streamingInterceptorConn struct {
35+
connect.StreamingClientConn
36+
token string
37+
}
38+
39+
func (conn *streamingInterceptorConn) Send(m any) error {
40+
conn.RequestHeader().Add("Authorization", "Bearer "+conn.token)
41+
return conn.StreamingClientConn.Send(m)
42+
}
43+
44+
type loggingInterceptor struct {
45+
config *DialConfig
46+
}
47+
48+
func (i *loggingInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
49+
return connect.UnaryFunc(func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) {
50+
i.config.Log.Debug("intercept", "request procedure", request.Spec().Procedure, "body", request.Any())
51+
response, err := next(ctx, request)
52+
if err != nil {
53+
return nil, err
54+
}
55+
i.config.Log.Debug("intercept", "request procedure", request.Spec().Procedure, "response", response.Any())
56+
return response, err
57+
})
58+
}
59+
60+
func (i *loggingInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
61+
// TODO also log here
62+
return next
63+
}
64+
65+
func (i *loggingInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
66+
return next
67+
}

go/client/client.go

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

go/client/client_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"github.com/metal-stack/api/go/client"
2121
apiv2 "github.com/metal-stack/api/go/metalstack/api/v2"
2222
"github.com/metal-stack/api/go/metalstack/api/v2/apiv2connect"
23+
infrav2 "github.com/metal-stack/api/go/metalstack/infra/v2"
24+
"github.com/metal-stack/api/go/metalstack/infra/v2/infrav2connect"
2325
"github.com/stretchr/testify/require"
2426
)
2527

@@ -155,3 +157,74 @@ func (m *mockTokenService) Revoke(context.Context, *apiv2.TokenServiceRevokeRequ
155157
func (m *mockTokenService) Update(context.Context, *apiv2.TokenServiceUpdateRequest) (*apiv2.TokenServiceUpdateResponse, error) {
156158
panic("unimplemented")
157159
}
160+
161+
func Test_ClientInterceptors(t *testing.T) {
162+
var (
163+
bs = &mockBMCService{}
164+
mux = http.NewServeMux()
165+
log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
166+
ctx = t.Context()
167+
)
168+
169+
mux.Handle(infrav2connect.NewBMCServiceHandler(bs))
170+
server := httptest.NewTLSServer(mux)
171+
server.EnableHTTP2 = true
172+
defer func() {
173+
server.Close()
174+
}()
175+
176+
tokenString, err := generateToken(1 * time.Second)
177+
require.NoError(t, err)
178+
179+
c, err := client.New(&client.DialConfig{
180+
BaseURL: server.URL,
181+
Token: tokenString,
182+
Transport: server.Client().Transport,
183+
Log: log,
184+
})
185+
require.NoError(t, err)
186+
187+
// Synchronous call has authheader set
188+
resp, err := c.Infrav2().BMC().UpdateBMCInfo(ctx, &infrav2.UpdateBMCInfoRequest{})
189+
require.NoError(t, err)
190+
require.NotNil(t, resp)
191+
require.Equal(t, tokenString, bs.token)
192+
bs.token = ""
193+
194+
// Asynchronous call has authheader set
195+
stream, err := c.Infrav2().BMC().WaitForMachineEvent(ctx, &infrav2.WaitForMachineEventRequest{})
196+
require.NoError(t, err)
197+
require.NotNil(t, stream)
198+
require.Equal(t, tokenString, bs.token)
199+
}
200+
201+
type mockBMCService struct {
202+
token string
203+
}
204+
205+
func (m *mockBMCService) UpdateBMCInfo(ctx context.Context, _ *infrav2.UpdateBMCInfoRequest) (*infrav2.UpdateBMCInfoResponse, error) {
206+
callinfo, _ := connect.CallInfoForHandlerContext(ctx)
207+
authHeader := callinfo.RequestHeader().Get("Authorization")
208+
209+
_, token, found := strings.Cut(authHeader, "Bearer ")
210+
211+
if !found {
212+
return nil, fmt.Errorf("unable to extract token from header:%s", authHeader)
213+
}
214+
m.token = token
215+
return &infrav2.UpdateBMCInfoResponse{}, nil
216+
}
217+
218+
func (m *mockBMCService) WaitForMachineEvent(ctx context.Context, _ *infrav2.WaitForMachineEventRequest, stream *connect.ServerStream[infrav2.WaitForMachineEventResponse]) error {
219+
callinfo, _ := connect.CallInfoForHandlerContext(ctx)
220+
authHeader := callinfo.RequestHeader().Get("Authorization")
221+
222+
_, token, found := strings.Cut(authHeader, "Bearer ")
223+
224+
if !found {
225+
return fmt.Errorf("unable to extract token from header:%s", authHeader)
226+
}
227+
228+
m.token = token
229+
return stream.Send(&infrav2.WaitForMachineEventResponse{})
230+
}

0 commit comments

Comments
 (0)