Skip to content

Commit b8c20bd

Browse files
author
Andrew Farries
committed
Add UploadFile method to contentservice client
Make the `GetSignedUploadUrl` method unexported and remove it from the interface. Compress reports before uploading to obj storage * Stop writing the usage report to disk. * Compress the report in memory. * Upload the compressed report to object storage.
1 parent 2eff43c commit b8c20bd

File tree

3 files changed

+52
-29
lines changed

3 files changed

+52
-29
lines changed

components/usage/pkg/contentservice/client.go

+33-4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ package contentservice
77
import (
88
"context"
99
"fmt"
10+
"io"
11+
"net/http"
1012

13+
"github.com/gitpod-io/gitpod/common-go/log"
1114
"github.com/gitpod-io/gitpod/content-service/api"
1215
"google.golang.org/grpc"
1316
"google.golang.org/grpc/credentials/insecure"
1417
)
1518

1619
type Interface interface {
17-
GetSignedUploadUrl(ctx context.Context) (string, error)
20+
UploadFile(ctx context.Context, filename string, body io.Reader) error
1821
}
1922

2023
type Client struct {
@@ -25,7 +28,33 @@ func New(url string) *Client {
2528
return &Client{url: url}
2629
}
2730

28-
func (c *Client) GetSignedUploadUrl(ctx context.Context) (string, error) {
31+
func (c *Client) UploadFile(ctx context.Context, filename string, body io.Reader) error {
32+
url, err := c.getSignedUploadUrl(ctx, filename)
33+
if err != nil {
34+
return fmt.Errorf("failed to obtain signed upload URL: %w", err)
35+
}
36+
37+
req, err := http.NewRequest(http.MethodPut, url, body)
38+
if err != nil {
39+
return fmt.Errorf("failed to construct http request: %w", err)
40+
}
41+
42+
req.Header.Set("Content-Encoding", "gzip")
43+
44+
log.Infof("Uploading %q to object storage...", filename)
45+
resp, err := http.DefaultClient.Do(req)
46+
if err != nil {
47+
return fmt.Errorf("failed to make http request: %w", err)
48+
}
49+
if resp.StatusCode != http.StatusOK {
50+
return fmt.Errorf("unexpected http response code: %s", resp.Status)
51+
}
52+
log.Info("Upload complete")
53+
54+
return nil
55+
}
56+
57+
func (c *Client) getSignedUploadUrl(ctx context.Context, key string) (string, error) {
2958
conn, err := grpc.Dial(c.url, grpc.WithTransportCredentials(insecure.NewCredentials()))
3059
if err != nil {
3160
return "", fmt.Errorf("failed to dial content-service gRPC server: %w", err)
@@ -34,9 +63,9 @@ func (c *Client) GetSignedUploadUrl(ctx context.Context) (string, error) {
3463

3564
uc := api.NewUsageReportServiceClient(conn)
3665

37-
resp, err := uc.UploadURL(ctx, &api.UsageReportUploadURLRequest{Name: "some-name"})
66+
resp, err := uc.UploadURL(ctx, &api.UsageReportUploadURLRequest{Name: key})
3867
if err != nil {
39-
return "", fmt.Errorf("failed to obtain signed upload URL: %w", err)
68+
return "", fmt.Errorf("failed RPC to content service: %w", err)
4069
}
4170

4271
return resp.Url, nil

components/usage/pkg/contentservice/noop.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
package contentservice
66

7-
import "context"
7+
import (
8+
"context"
9+
"io"
10+
)
811

912
type NoOpClient struct{}
1013

11-
func (c *NoOpClient) GetSignedUploadUrl(ctx context.Context) (string, error) { return "", nil }
14+
func (c *NoOpClient) UploadFile(ctx context.Context, filename string, body io.Reader) error {
15+
return nil
16+
}

components/usage/pkg/controller/reconciler.go

+12-23
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
package controller
66

77
import (
8+
"bytes"
9+
"compress/gzip"
810
"context"
911
"database/sql"
1012
"encoding/json"
1113
"fmt"
12-
"io/ioutil"
1314
"math"
14-
"os"
15-
"path/filepath"
1615
"time"
1716

1817
"github.com/gitpod-io/gitpod/common-go/log"
@@ -76,36 +75,26 @@ func (u *UsageReconciler) Reconcile() (err error) {
7675
}
7776
log.WithField("usage_reconcile_status", status).Info("Reconcile completed.")
7877

79-
// For now, write the report to a temp directory so we can manually retrieve it
80-
dir := os.TempDir()
81-
f, err := ioutil.TempFile(dir, fmt.Sprintf("%s-*", now.Format(time.RFC3339)))
82-
if err != nil {
83-
return fmt.Errorf("failed to create temporary file: %w", err)
84-
}
85-
defer f.Close()
86-
87-
enc := json.NewEncoder(f)
88-
err = enc.Encode(report)
78+
reportBytes := &bytes.Buffer{}
79+
gz := gzip.NewWriter(reportBytes)
80+
err = json.NewEncoder(gz).Encode(report)
8981
if err != nil {
9082
return fmt.Errorf("failed to marshal report to JSON: %w", err)
9183
}
92-
93-
stat, err := f.Stat()
94-
filePath := filepath.Join(dir, stat.Name())
84+
err = gz.Close()
9585
if err != nil {
96-
return fmt.Errorf("failed to get file stats: %w", err)
86+
return fmt.Errorf("failed to compress usage report: %w", err)
9787
}
98-
log.Infof("Wrote usage report into %s", filePath)
9988

100-
uploadURL, err := u.contentService.GetSignedUploadUrl(ctx)
89+
err = db.CreateUsageRecords(ctx, u.conn, usageReportToUsageRecords(report, u.pricer, u.nowFunc().UTC()))
10190
if err != nil {
102-
return fmt.Errorf("failed to obtain signed upload URL: %w", err)
91+
return fmt.Errorf("failed to write usage records to database: %s", err)
10392
}
104-
log.Infof("signed upload url: %s", uploadURL)
10593

106-
err = db.CreateUsageRecords(ctx, u.conn, usageReportToUsageRecords(report, u.pricer, u.nowFunc().UTC()))
94+
filename := fmt.Sprintf("%s.gz", now.Format(time.RFC3339))
95+
err = u.contentService.UploadFile(ctx, filename, reportBytes)
10796
if err != nil {
108-
return fmt.Errorf("failed to write usage records to database: %s", err)
97+
return fmt.Errorf("failed to upload usage report: %w", err)
10998
}
11099

111100
return nil

0 commit comments

Comments
 (0)