Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/content-service/pkg/service/headless-log-service.go
2499 views
1
// Copyright (c) 2021 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 service
6
7
import (
8
"context"
9
"strings"
10
11
"github.com/opentracing/opentracing-go"
12
"golang.org/x/xerrors"
13
"google.golang.org/grpc/codes"
14
"google.golang.org/grpc/status"
15
16
"github.com/gitpod-io/gitpod/common-go/log"
17
"github.com/gitpod-io/gitpod/common-go/tracing"
18
"github.com/gitpod-io/gitpod/content-service/api"
19
"github.com/gitpod-io/gitpod/content-service/api/config"
20
"github.com/gitpod-io/gitpod/content-service/pkg/logs"
21
"github.com/gitpod-io/gitpod/content-service/pkg/storage"
22
)
23
24
// HeadlessLogService implements LogServiceServer
25
type HeadlessLogService struct {
26
cfg config.StorageConfig
27
s storage.PresignedAccess
28
daFactory func(cfg *config.StorageConfig) (storage.DirectAccess, error)
29
30
api.UnimplementedHeadlessLogServiceServer
31
}
32
33
// NewHeadlessLogService create a new content service
34
func NewHeadlessLogService(cfg config.StorageConfig) (res *HeadlessLogService, err error) {
35
s, err := storage.NewPresignedAccess(&cfg)
36
if err != nil {
37
return nil, err
38
}
39
daFactory := func(cfg *config.StorageConfig) (storage.DirectAccess, error) {
40
return storage.NewDirectAccess(cfg)
41
}
42
return &HeadlessLogService{
43
cfg: cfg,
44
s: s,
45
daFactory: daFactory,
46
}, nil
47
}
48
49
// LogDownloadURL provides a URL from where the content of a workspace log stream can be downloaded from
50
func (ls *HeadlessLogService) LogDownloadURL(ctx context.Context, req *api.LogDownloadURLRequest) (resp *api.LogDownloadURLResponse, err error) {
51
span, ctx := opentracing.StartSpanFromContext(ctx, "WorkspaceDownloadURL")
52
span.SetTag("user", req.OwnerId)
53
span.SetTag("workspaceId", req.WorkspaceId)
54
span.SetTag("instanceId", req.InstanceId)
55
defer tracing.FinishSpan(span, &err)
56
57
blobName := ls.s.InstanceObject(req.OwnerId, req.WorkspaceId, req.InstanceId, logs.UploadedHeadlessLogPath(req.TaskId))
58
info, err := ls.s.SignDownload(ctx, ls.s.Bucket(req.OwnerId), blobName, &storage.SignedURLOptions{})
59
if err != nil {
60
log.WithFields(log.OWI(req.OwnerId, req.WorkspaceId, "")).
61
WithField("bucket", ls.s.Bucket(req.OwnerId)).
62
WithField("blobName", blobName).
63
WithError(err).
64
Error("error getting SignDownload URL")
65
if err == storage.ErrNotFound {
66
return nil, status.Error(codes.NotFound, err.Error())
67
}
68
return nil, status.Error(codes.Unknown, err.Error())
69
}
70
71
return &api.LogDownloadURLResponse{
72
Url: info.URL,
73
}, nil
74
}
75
76
// ListLogs returns a list of taskIds for the specified workspace instance
77
func (ls *HeadlessLogService) ListLogs(ctx context.Context, req *api.ListLogsRequest) (resp *api.ListLogsResponse, err error) {
78
da, err := ls.daFactory(&ls.cfg)
79
if err != nil {
80
return nil, xerrors.Errorf("cannot use configured storage: %w", err)
81
}
82
83
err = da.Init(ctx, req.OwnerId, req.WorkspaceId, req.InstanceId)
84
if err != nil {
85
return nil, xerrors.Errorf("cannot use configured storage: %w", err)
86
}
87
// we do not need to check whether the bucket exists because ListObjects() does that for us
88
89
// all files under this prefix are headless log files, named after their respective taskId
90
prefix := ls.s.InstanceObject(req.OwnerId, req.WorkspaceId, req.InstanceId, logs.UploadedHeadlessLogPathPrefix)
91
objects, err := da.ListObjects(ctx, prefix)
92
if err != nil {
93
return nil, err
94
}
95
96
var taskIds []string
97
for _, obj := range objects {
98
ss := strings.Split(obj, "/")
99
taskId := ss[len(ss)-1]
100
taskIds = append(taskIds, taskId)
101
}
102
103
return &api.ListLogsResponse{
104
TaskId: taskIds,
105
}, nil
106
}
107
108