package services import ( "context" "fmt" "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" ) var S3Client S3 type S3 interface { GetPresignedURL(ctx context.Context, key, contentType string, expiry time.Duration) (string, error) DoesFileExist(ctx context.Context, key string) (bool, error) } type S3Concrete struct { S3Client *s3.Client Bucket string } func (svc S3Concrete) GetPresignedURL(ctx context.Context, key, contentType string, expiry time.Duration) (string, error) { presignClient := s3.NewPresignClient(svc.S3Client) request, err := presignClient.PresignPutObject(ctx, &s3.PutObjectInput{ Bucket: &svc.Bucket, Key: &key, ContentType: &contentType, }, s3.WithPresignExpires(expiry)) if err != nil { return "", err } return request.URL, nil } func (svc S3Concrete) DoesFileExist(ctx context.Context, key string) (bool, error) { _, error := svc.S3Client.HeadObject(ctx, &s3.HeadObjectInput{ Bucket: &svc.Bucket, Key: &key, }) if error != nil { return false, error } return true, nil } // AssertSuccessfulConnection verifies that the bucket is reachable and that // the configured credentials can get, put, and presign objects in it. It // panics with a descriptive message on the first check that fails, since the // service cannot function without these permissions. func (svc S3Concrete) AssertSuccessfulConnection(ctx context.Context) { if _, err := svc.S3Client.HeadBucket(ctx, &s3.HeadBucketInput{ Bucket: &svc.Bucket, }); err != nil { panic(fmt.Errorf("s3: cannot connect to bucket %q: %w", svc.Bucket, err)) } const probeKey = ".s3-connectivity-check" if _, err := svc.S3Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: &svc.Bucket, Key: aws.String(probeKey), Body: strings.NewReader("ok"), }); err != nil { panic(fmt.Errorf("s3: missing permission to put objects in bucket %q: %w", svc.Bucket, err)) } object, err := svc.S3Client.GetObject(ctx, &s3.GetObjectInput{ Bucket: &svc.Bucket, Key: aws.String(probeKey), }) if err != nil { panic(fmt.Errorf("s3: missing permission to get objects from bucket %q: %w", svc.Bucket, err)) } object.Body.Close() if _, err := svc.GetPresignedURL(ctx, probeKey, "text/plain", time.Minute); err != nil { panic(fmt.Errorf("s3: cannot create presigned urls for bucket %q: %w", svc.Bucket, err)) } }