Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ws-manager-mk2/pkg/proxy/imagebuilder.go
2500 views
1
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package proxy
6
7
import (
8
"context"
9
"errors"
10
"io"
11
12
"github.com/gitpod-io/gitpod/common-go/log"
13
"github.com/gitpod-io/gitpod/image-builder/api"
14
15
"google.golang.org/grpc/codes"
16
"google.golang.org/grpc/status"
17
"google.golang.org/protobuf/proto"
18
)
19
20
type ImageBuilder struct {
21
D api.ImageBuilderClient
22
23
api.UnimplementedImageBuilderServer
24
}
25
26
func (p ImageBuilder) ResolveBaseImage(ctx context.Context, req *api.ResolveBaseImageRequest) (*api.ResolveBaseImageResponse, error) {
27
return p.D.ResolveBaseImage(ctx, req)
28
}
29
30
func (p ImageBuilder) ResolveWorkspaceImage(ctx context.Context, req *api.ResolveWorkspaceImageRequest) (*api.ResolveWorkspaceImageResponse, error) {
31
return p.D.ResolveWorkspaceImage(ctx, req)
32
}
33
34
func (p ImageBuilder) Build(req *api.BuildRequest, srv api.ImageBuilder_BuildServer) error {
35
c, err := p.D.Build(srv.Context(), req)
36
if err != nil {
37
return err
38
}
39
defer c.CloseSend()
40
41
return forwardStream(srv.Context(), c.Recv, srv.Send)
42
}
43
44
func (p ImageBuilder) Logs(req *api.LogsRequest, srv api.ImageBuilder_LogsServer) error {
45
c, err := p.D.Logs(srv.Context(), req)
46
if err != nil {
47
return err
48
}
49
defer c.CloseSend()
50
51
return forwardStream(srv.Context(), c.Recv, srv.Send)
52
}
53
54
func (p ImageBuilder) ListBuilds(ctx context.Context, req *api.ListBuildsRequest) (*api.ListBuildsResponse, error) {
55
return p.D.ListBuilds(ctx, req)
56
}
57
58
type ProtoMessage interface {
59
proto.Message
60
comparable
61
}
62
63
func forwardStream[R ProtoMessage](ctx context.Context, recv func() (R, error), send func(R) error) error {
64
for {
65
resp, err := recv()
66
if err != nil {
67
return handleProxyError(err)
68
}
69
70
// generic hack, can't compare R to nil because R's default value is unclear (not even sure this is nil)
71
// But, we can get the default value which will be nil because underneath R is an interface.
72
var defaultResp R
73
if resp == defaultResp {
74
break
75
}
76
err = send(resp)
77
if err != nil {
78
return handleProxyError(err)
79
}
80
}
81
82
return nil
83
}
84
85
// handleProxyError ensures all errors have proper gRPC status codes
86
func handleProxyError(err error) error {
87
if err == nil {
88
return nil
89
}
90
91
// If it's already a gRPC status error, check for DeadlineExceeded
92
if st, ok := status.FromError(err); ok {
93
if st.Code() == codes.DeadlineExceeded {
94
// Return nil (OK) for DeadlineExceeded as requested
95
return nil
96
}
97
98
log.WithError(err).WithField("code", status.Code(err)).Error("unexpected error while sending stream response upstream")
99
return err
100
}
101
102
// Handle context errors
103
if errors.Is(err, context.DeadlineExceeded) {
104
// Return nil (OK) for DeadlineExceeded
105
return nil
106
}
107
108
if errors.Is(err, io.EOF) {
109
// Return nil (OK) for EOF, which is a normal when the client ends the stream
110
return nil
111
}
112
113
log.WithError(err).Error("unexpected error while sending stream response upstream")
114
115
if errors.Is(err, context.Canceled) {
116
return status.Error(codes.Canceled, err.Error())
117
}
118
119
// Wrap any other error as Internal
120
return status.Error(codes.Internal, err.Error())
121
}
122
123