Skip to content

Commit 0a476c5

Browse files
easyCZroboquat
authored andcommitted
[public-api] U Refactor to use connect handlers, route to HTTP server
1 parent e46481a commit 0a476c5

File tree

10 files changed

+195
-293
lines changed

10 files changed

+195
-293
lines changed

components/proxy/conf/Caddyfile

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,7 @@ api.{$GITPOD_DOMAIN} {
150150
output stdout
151151
}
152152

153-
@grpc protocol grpc
154-
155-
handle @grpc {
156-
# gRPC traffic goes to gRPC server
157-
reverse_proxy h2c://public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9001
158-
}
159-
160-
# Non-grpc traffic goes to an HTTP server
153+
# All traffic goes to HTTP endpoint. We handle gRPC using connect.build
161154
reverse_proxy public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9002
162155
}
163156

components/public-api-server/pkg/apiv1/prebuild.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,24 @@ package apiv1
66

77
import (
88
"context"
9+
10+
"github.com/bufbuild/connect-go"
911
v1 "github.com/gitpod-io/gitpod/public-api/v1"
12+
"github.com/gitpod-io/gitpod/public-api/v1/v1connect"
1013
)
1114

1215
func NewPrebuildService() *PrebuildService {
13-
return &PrebuildService{
14-
UnimplementedPrebuildsServiceServer: &v1.UnimplementedPrebuildsServiceServer{},
15-
}
16+
return &PrebuildService{}
1617
}
1718

1819
type PrebuildService struct {
19-
*v1.UnimplementedPrebuildsServiceServer
20+
v1connect.UnimplementedPrebuildsServiceHandler
2021
}
2122

22-
func (p *PrebuildService) GetPrebuild(ctx context.Context, req *v1.GetPrebuildRequest) (*v1.GetPrebuildResponse, error) {
23-
return &v1.GetPrebuildResponse{
23+
func (p *PrebuildService) GetPrebuild(ctx context.Context, req *connect.Request[v1.GetPrebuildRequest]) (*connect.Response[v1.GetPrebuildResponse], error) {
24+
return connect.NewResponse(&v1.GetPrebuildResponse{
2425
Prebuild: &v1.Prebuild{
25-
PrebuildId: req.GetPrebuildId(),
26+
PrebuildId: req.Msg.GetPrebuildId(),
2627
Spec: &v1.PrebuildSpec{
2728
Context: &v1.WorkspaceContext{
2829
ContextUrl: "https://github.com/gitpod-io/gitpod",
@@ -32,5 +33,5 @@ func (p *PrebuildService) GetPrebuild(ctx context.Context, req *v1.GetPrebuildRe
3233
},
3334
Status: nil,
3435
},
35-
}, nil
36+
}), nil
3637
}

components/public-api-server/pkg/apiv1/prebuild_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ package apiv1
66

77
import (
88
"context"
9+
"testing"
10+
11+
"github.com/bufbuild/connect-go"
912
v1 "github.com/gitpod-io/gitpod/public-api/v1"
1013
"github.com/stretchr/testify/require"
11-
"testing"
1214
)
1315

1416
func TestPrebuildService_GetPrebuild(t *testing.T) {
1517
svc := NewPrebuildService()
1618

1719
prebuildID := "some-prebuild-id"
18-
resp, err := svc.GetPrebuild(context.Background(), &v1.GetPrebuildRequest{
20+
resp, err := svc.GetPrebuild(context.Background(), connect.NewRequest(&v1.GetPrebuildRequest{
1921
PrebuildId: prebuildID,
20-
})
22+
}))
2123
require.NoError(t, err)
2224
require.Equal(t, &v1.GetPrebuildResponse{
2325
Prebuild: &v1.Prebuild{
@@ -31,6 +33,6 @@ func TestPrebuildService_GetPrebuild(t *testing.T) {
3133
},
3234
Status: nil,
3335
},
34-
}, resp)
36+
}, resp.Msg)
3537

3638
}

components/public-api-server/pkg/apiv1/workspace.go

Lines changed: 34 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -6,60 +6,48 @@ package apiv1
66

77
import (
88
"context"
9+
"fmt"
910

11+
connect "github.com/bufbuild/connect-go"
1012
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
13+
"github.com/gitpod-io/gitpod/public-api-server/pkg/auth"
1114
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
1215
v1 "github.com/gitpod-io/gitpod/public-api/v1"
16+
"github.com/gitpod-io/gitpod/public-api/v1/v1connect"
1317
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
1418
"github.com/relvacode/iso8601"
15-
"google.golang.org/grpc"
16-
"google.golang.org/grpc/codes"
17-
"google.golang.org/grpc/metadata"
18-
"google.golang.org/grpc/status"
1919
"google.golang.org/protobuf/types/known/timestamppb"
2020
)
2121

2222
func NewWorkspaceService(serverConnPool proxy.ServerConnectionPool) *WorkspaceService {
2323
return &WorkspaceService{
24-
connectionPool: serverConnPool,
25-
UnimplementedWorkspacesServiceServer: &v1.UnimplementedWorkspacesServiceServer{},
24+
connectionPool: serverConnPool,
2625
}
2726
}
2827

2928
type WorkspaceService struct {
3029
connectionPool proxy.ServerConnectionPool
3130

32-
*v1.UnimplementedWorkspacesServiceServer
31+
v1connect.UnimplementedWorkspacesServiceHandler
3332
}
3433

35-
func (w *WorkspaceService) GetWorkspace(ctx context.Context, r *v1.GetWorkspaceRequest) (*v1.GetWorkspaceResponse, error) {
34+
func (s *WorkspaceService) GetWorkspace(ctx context.Context, req *connect.Request[v1.GetWorkspaceRequest]) (*connect.Response[v1.GetWorkspaceResponse], error) {
35+
token := auth.TokenFromContext(ctx)
3636
logger := ctxlogrus.Extract(ctx)
37-
token, err := bearerTokenFromContext(ctx)
38-
if err != nil {
39-
return nil, err
40-
}
4137

42-
server, err := w.connectionPool.Get(ctx, token)
38+
server, err := s.connectionPool.Get(ctx, token)
4339
if err != nil {
4440
logger.WithError(err).Error("Failed to get connection to server.")
45-
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
41+
return nil, connect.NewError(connect.CodeInternal, err)
4642
}
4743

48-
workspace, err := server.GetWorkspace(ctx, r.GetWorkspaceId())
44+
workspace, err := server.GetWorkspace(ctx, req.Msg.GetWorkspaceId())
4945
if err != nil {
5046
logger.WithError(err).Error("Failed to get workspace.")
51-
converted := proxy.ConvertError(err)
52-
switch status.Code(converted) {
53-
case codes.PermissionDenied:
54-
return nil, status.Error(codes.PermissionDenied, "insufficient permission to access workspace")
55-
case codes.NotFound:
56-
return nil, status.Error(codes.NotFound, "workspace does not exist")
57-
default:
58-
return nil, status.Error(codes.Internal, "unable to retrieve workspace")
59-
}
47+
return nil, proxy.ConvertError(err)
6048
}
6149

62-
return &v1.GetWorkspaceResponse{
50+
return connect.NewResponse(&v1.GetWorkspaceResponse{
6351
Result: &v1.Workspace{
6452
WorkspaceId: workspace.Workspace.ID,
6553
OwnerId: workspace.Workspace.OwnerID,
@@ -73,72 +61,40 @@ func (w *WorkspaceService) GetWorkspace(ctx context.Context, r *v1.GetWorkspaceR
7361
},
7462
Description: workspace.Workspace.Description,
7563
},
76-
}, nil
64+
}), nil
7765
}
7866

79-
func (w *WorkspaceService) GetOwnerToken(ctx context.Context, r *v1.GetOwnerTokenRequest) (*v1.GetOwnerTokenResponse, error) {
67+
func (s *WorkspaceService) GetOwnerToken(ctx context.Context, req *connect.Request[v1.GetOwnerTokenRequest]) (*connect.Response[v1.GetOwnerTokenResponse], error) {
8068
logger := ctxlogrus.Extract(ctx)
81-
token, err := bearerTokenFromContext(ctx)
82-
if err != nil {
83-
return nil, err
84-
}
69+
token := auth.TokenFromContext(ctx)
8570

86-
server, err := w.connectionPool.Get(ctx, token)
71+
server, err := s.connectionPool.Get(ctx, token)
8772
if err != nil {
8873
logger.WithError(err).Error("Failed to get connection to server.")
89-
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
74+
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to establish connection to downstream services"))
9075
}
9176

92-
ownerToken, err := server.GetOwnerToken(ctx, r.GetWorkspaceId())
77+
ownerToken, err := server.GetOwnerToken(ctx, req.Msg.GetWorkspaceId())
9378

9479
if err != nil {
9580
logger.WithError(err).Error("Failed to get owner token.")
96-
converted := proxy.ConvertError(err)
97-
switch status.Code(converted) {
98-
case codes.PermissionDenied:
99-
return nil, status.Error(codes.PermissionDenied, "insufficient permission to retrieve ownertoken")
100-
case codes.NotFound:
101-
return nil, status.Error(codes.NotFound, "workspace does not exist")
102-
default:
103-
return nil, status.Error(codes.Internal, "unable to retrieve owner token")
104-
}
105-
}
106-
107-
return &v1.GetOwnerTokenResponse{Token: ownerToken}, nil
108-
}
109-
110-
func bearerTokenFromContext(ctx context.Context) (string, error) {
111-
md, ok := metadata.FromIncomingContext(ctx)
112-
if !ok {
113-
return "", status.Error(codes.Unauthenticated, "no credentials provided")
114-
}
115-
116-
values := md.Get("authorization")
117-
if len(values) == 0 {
118-
return "", status.Error(codes.Unauthenticated, "no authorization header specified")
119-
}
120-
if len(values) > 1 {
121-
return "", status.Error(codes.Unauthenticated, "more than one authorization header specified, exactly one is required")
81+
return nil, proxy.ConvertError(err)
12282
}
12383

124-
token := values[0]
125-
return token, nil
84+
return connect.NewResponse(&v1.GetOwnerTokenResponse{Token: ownerToken}), nil
12685
}
12786

128-
func (w *WorkspaceService) ListWorkspaces(ctx context.Context, req *v1.ListWorkspacesRequest) (*v1.ListWorkspacesResponse, error) {
87+
func (s *WorkspaceService) ListWorkspaces(ctx context.Context, req *connect.Request[v1.ListWorkspacesRequest]) (*connect.Response[v1.ListWorkspacesResponse], error) {
12988
logger := ctxlogrus.Extract(ctx)
130-
token, err := bearerTokenFromContext(ctx)
131-
if err != nil {
132-
return nil, err
133-
}
89+
token := auth.TokenFromContext(ctx)
13490

135-
server, err := w.connectionPool.Get(ctx, token)
91+
server, err := s.connectionPool.Get(ctx, token)
13692
if err != nil {
13793
logger.WithError(err).Error("Failed to get connection to server.")
138-
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
94+
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to establish connection to downstream services"))
13995
}
14096

141-
limit, err := getLimitFromPagination(req.Pagination)
97+
limit, err := getLimitFromPagination(req.Msg.GetPagination())
14298
if err != nil {
14399
// getLimitFromPagination returns gRPC errors
144100
return nil, err
@@ -161,9 +117,11 @@ func (w *WorkspaceService) ListWorkspaces(ctx context.Context, req *v1.ListWorks
161117
res = append(res, workspaceAndInstance)
162118
}
163119

164-
return &v1.ListWorkspacesResponse{
165-
Result: res,
166-
}, nil
120+
return connect.NewResponse(
121+
&v1.ListWorkspacesResponse{
122+
Result: res,
123+
},
124+
), nil
167125
}
168126

169127
func getLimitFromPagination(pagination *v1.Pagination) (int, error) {
@@ -179,7 +137,7 @@ func getLimitFromPagination(pagination *v1.Pagination) (int, error) {
179137
return defaultLimit, nil
180138
}
181139
if pagination.PageSize < 0 || maxLimit < pagination.PageSize {
182-
return 0, grpc.Errorf(codes.InvalidArgument, "invalid pagination page size (must be 0 < x < %d)", maxLimit)
140+
return 0, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid pagination page size (must be 0 < x < %d)", maxLimit))
183141
}
184142

185143
return int(pagination.PageSize), nil
@@ -192,7 +150,7 @@ func convertWorkspaceInfo(input *protocol.WorkspaceInfo) (*v1.ListWorkspacesResp
192150
creationTime, err := parseGitpodTimestamp(wsi.CreationTime)
193151
if err != nil {
194152
// TODO(cw): should this really return an error and possibly fail the entire operation?
195-
return nil, grpc.Errorf(codes.FailedPrecondition, "cannot parse creation time: %v", err)
153+
return nil, connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("cannot parse creation time: %v", err))
196154
}
197155

198156
var phase v1.WorkspaceInstanceStatus_Phase
@@ -219,7 +177,7 @@ func convertWorkspaceInfo(input *protocol.WorkspaceInfo) (*v1.ListWorkspacesResp
219177
phase = v1.WorkspaceInstanceStatus_PHASE_STOPPED
220178
default:
221179
// TODO(cw): should this really return an error and possibly fail the entire operation?
222-
return nil, grpc.Errorf(codes.FailedPrecondition, "cannot convert instance phase: %s", wsi.Status.Phase)
180+
return nil, connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("cannot convert instance phase: %s", wsi.Status.Phase))
223181
}
224182

225183
var admissionLevel v1.AdmissionLevel

0 commit comments

Comments
 (0)