Path: blob/main/components/content-service/pkg/service/blob-service.go
2499 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package service56import (7"context"8"strings"910"github.com/opentracing/opentracing-go"11"google.golang.org/grpc/codes"12"google.golang.org/grpc/status"1314"github.com/gitpod-io/gitpod/common-go/log"15"github.com/gitpod-io/gitpod/common-go/tracing"16"github.com/gitpod-io/gitpod/content-service/api"17config "github.com/gitpod-io/gitpod/content-service/api/config"18"github.com/gitpod-io/gitpod/content-service/pkg/storage"19)2021// BlobService implements BlobServiceServer22type BlobService struct {23cfg config.StorageConfig24s storage.PresignedAccess2526api.UnimplementedBlobServiceServer27}2829// NewBlobService create a new content service30func NewBlobService(cfg config.StorageConfig) (res *BlobService, err error) {31s, err := storage.NewPresignedAccess(&cfg)32if err != nil {33return nil, err34}35return &BlobService{cfg: cfg, s: s}, nil36}3738// UploadUrl provides a upload URL39func (cs *BlobService) UploadUrl(ctx context.Context, req *api.UploadUrlRequest) (resp *api.UploadUrlResponse, err error) {40//nolint:ineffassign41span, ctx := opentracing.StartSpanFromContext(ctx, "UploadUrl")42span.SetTag("user", req.OwnerId)43span.SetTag("name", req.Name)44defer tracing.FinishSpan(span, &err)4546bucket := cs.s.Bucket(req.OwnerId)4748err = cs.s.EnsureExists(ctx, bucket)49if err != nil {50return nil, status.Error(codes.NotFound, err.Error())51}5253blobName, err := cs.s.BlobObject(req.OwnerId, req.Name)54if err != nil {55return nil, status.Error(codes.InvalidArgument, err.Error())56}5758if cs.cfg.BlobQuota > 0 {59prefix := strings.Split(blobName, "/")[0]60size, err := cs.s.DiskUsage(ctx, bucket, prefix)6162if err != nil {63return nil, status.Error(codes.Unknown, err.Error())64}65exceeded := size >= cs.cfg.BlobQuota66log.WithFields(log.OWI(req.OwnerId, "", "")).Debugf("checking blob quota - quota: %d, size: %d, exceeded: %t", cs.cfg.BlobQuota, size, exceeded)67if exceeded {68return nil, status.Error(codes.ResourceExhausted, "quota exceeded")69}70} else {71log.Debug("blob quota disabled")72}7374info, err := cs.s.SignUpload(ctx, bucket, blobName, &storage.SignedURLOptions{75ContentType: req.ContentType,76})77if err != nil {78log.Error("error getting SignUpload URL: ", err)79if err == storage.ErrNotFound {80return nil, status.Error(codes.NotFound, err.Error())81}82return nil, status.Error(codes.Unknown, err.Error())83}8485return &api.UploadUrlResponse{86Url: info.URL,87}, nil88}8990// DownloadUrl provides a download URL91func (cs *BlobService) DownloadUrl(ctx context.Context, req *api.DownloadUrlRequest) (resp *api.DownloadUrlResponse, err error) {92//nolint:ineffassign93span, ctx := opentracing.StartSpanFromContext(ctx, "DownloadUrl")94span.SetTag("user", req.OwnerId)95span.SetTag("name", req.Name)96defer tracing.FinishSpan(span, &err)9798blobName, err := cs.s.BlobObject(req.OwnerId, req.Name)99if err != nil {100return nil, status.Error(codes.InvalidArgument, err.Error())101}102103info, err := cs.s.SignDownload(ctx, cs.s.Bucket(req.OwnerId), blobName, &storage.SignedURLOptions{104ContentType: req.ContentType,105})106if err != nil {107log.Error("error getting SignDownload URL: ", err)108if err == storage.ErrNotFound {109return nil, status.Error(codes.NotFound, err.Error())110}111return nil, status.Error(codes.Unknown, err.Error())112}113114return &api.DownloadUrlResponse{115Url: info.URL,116}, nil117}118119// Delete deletes the uploaded content120func (cs *BlobService) Delete(ctx context.Context, req *api.DeleteRequest) (resp *api.DeleteResponse, err error) {121//nolint:ineffassign122span, ctx := opentracing.StartSpanFromContext(ctx, "Delete")123span.SetTag("user", req.OwnerId)124span.SetTag("name", req.Name)125defer tracing.FinishSpan(span, &err)126127var query *storage.DeleteObjectQuery128exact := req.GetExact()129prefix := req.GetPrefix()130if exact != "" {131exact, err = cs.s.BlobObject(req.OwnerId, exact)132if err != nil {133return nil, status.Error(codes.InvalidArgument, err.Error())134}135query = &storage.DeleteObjectQuery{Name: exact}136} else if prefix != "" {137prefix, err = cs.s.BlobObject(req.OwnerId, prefix)138if err != nil {139return nil, status.Error(codes.InvalidArgument, err.Error())140}141query = &storage.DeleteObjectQuery{Prefix: prefix}142} else {143return nil, status.Error(codes.InvalidArgument, "Name arg is missing")144}145146bucket := cs.s.Bucket(req.OwnerId)147148err = cs.s.DeleteObject(ctx, bucket, query)149if err == storage.ErrNotFound {150return nil, status.Error(codes.NotFound, err.Error())151}152if err != nil {153return nil, status.Error(codes.Unknown, err.Error())154}155resp = &api.DeleteResponse{}156return resp, nil157}158159160