Skip to content

Commit bf69a5c

Browse files
authored
fix(grpc): retry transient errors when teams service is unavailable (#225)
Previously, repository authorization checks would return the PermissionDenied code if the downstream API returned any error response, regardless of actual authorization status. This commit separates errors from the downstream API and the actual authorization state, returning the Unavailable code instead for the former case. This allows for deploy clients to automatically retry transient errors.
1 parent cb2604a commit bf69a5c

File tree

3 files changed

+17
-8
lines changed

3 files changed

+17
-8
lines changed

pkg/grpc/interceptor/auth/server.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type TokenValidator interface {
3636
}
3737

3838
type TeamsClient interface {
39-
IsAuthorized(ctx context.Context, repo, team string) bool
39+
IsAuthorized(ctx context.Context, repo, team string) (bool, error)
4040
}
4141

4242
type authData struct {
@@ -87,7 +87,12 @@ func (s *ServerInterceptor) UnaryServerInterceptor(ctx context.Context, req inte
8787
return nil, status.Errorf(codes.InvalidArgument, "missing team in metadata")
8888
}
8989

90-
if !s.TeamsClient.IsAuthorized(ctx, repo, team) {
90+
authorized, err := s.TeamsClient.IsAuthorized(ctx, repo, team)
91+
if err != nil {
92+
metrics.InterceptorRequest(requestTypeJWT, "teams_service_error")
93+
return nil, status.Errorf(codes.Unavailable, "something wrong happened when communicating with the teams service")
94+
}
95+
if !authorized {
9196
metrics.InterceptorRequest(requestTypeJWT, "repo_not_authorized")
9297
return nil, status.Errorf(codes.PermissionDenied, fmt.Sprintf("repo %q not authorized by team %q", repo, team))
9398
}
@@ -180,7 +185,11 @@ func (s *ServerInterceptor) StreamServerInterceptor(srv interface{}, ss grpc.Ser
180185
return status.Errorf(codes.InvalidArgument, "missing team in metadata")
181186
}
182187

183-
if !s.TeamsClient.IsAuthorized(ss.Context(), repo, team) {
188+
authorized, err := s.TeamsClient.IsAuthorized(ss.Context(), repo, team)
189+
if err != nil {
190+
return status.Errorf(codes.Unavailable, "something wrong happened when communicating with the teams service")
191+
}
192+
if !authorized {
184193
return status.Errorf(codes.PermissionDenied, fmt.Sprintf("repo %q not authorized by team %q", repo, team))
185194
}
186195
} else {

pkg/grpc/interceptor/auth/server_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ type mockTeamsClient struct {
180180
authorized map[string]string
181181
}
182182

183-
func (m *mockTeamsClient) IsAuthorized(ctx context.Context, repo, team string) bool {
184-
return m.authorized[repo] == team
183+
func (m *mockTeamsClient) IsAuthorized(ctx context.Context, repo, team string) (bool, error) {
184+
return m.authorized[repo] == team, nil
185185
}
186186

187187
func handler(ctx context.Context, req any) (any, error) {

pkg/naisapi/naisapi.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ func NewClient(target string, insecureConnection bool) (*Client, error) {
3030
}, nil
3131
}
3232

33-
func (c *Client) IsAuthorized(ctx context.Context, repo, team string) bool {
33+
func (c *Client) IsAuthorized(ctx context.Context, repo, team string) (bool, error) {
3434
resp, err := c.client.IsRepositoryAuthorized(ctx, &protoapi.IsRepositoryAuthorizedRequest{
3535
TeamSlug: team,
3636
Repository: repo,
3737
})
3838
if err != nil {
3939
log.WithError(err).Error("checking repo authorization in teams")
40-
return false
40+
return false, err
4141
}
4242

43-
return resp.IsAuthorized
43+
return resp.IsAuthorized, nil
4444
}

0 commit comments

Comments
 (0)