-
Notifications
You must be signed in to change notification settings - Fork 55
/
gcs.go
131 lines (107 loc) · 3.77 KB
/
gcs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
// Package gcs implements a storage backend for the KMS using Google Cloud Storage (GCS).
package gcs
import (
"context"
"errors"
"io"
gcstorage "cloud.google.com/go/storage"
"github.com/edgelesssys/constellation/v2/internal/kms/storage"
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
"google.golang.org/api/option"
)
type gcpStorageAPI interface {
Attrs(ctx context.Context, bucketName string) (*gcstorage.BucketAttrs, error)
Close() error
CreateBucket(ctx context.Context, bucketName, projectID string, attrs *gcstorage.BucketAttrs) error
NewWriter(ctx context.Context, bucketName, objectName string) io.WriteCloser
NewReader(ctx context.Context, bucketName, objectName string) (io.ReadCloser, error)
}
type wrappedGCPClient struct {
*gcstorage.Client
}
func (c *wrappedGCPClient) Attrs(ctx context.Context, bucketName string) (*gcstorage.BucketAttrs, error) {
return c.Client.Bucket(bucketName).Attrs(ctx)
}
func (c *wrappedGCPClient) CreateBucket(ctx context.Context, bucketName, projectID string, attrs *gcstorage.BucketAttrs) error {
return c.Client.Bucket(bucketName).Create(ctx, projectID, attrs)
}
func (c *wrappedGCPClient) NewWriter(ctx context.Context, bucketName, objectName string) io.WriteCloser {
return c.Client.Bucket(bucketName).Object(objectName).NewWriter(ctx)
}
func (c *wrappedGCPClient) NewReader(ctx context.Context, bucketName, objectName string) (io.ReadCloser, error) {
return c.Client.Bucket(bucketName).Object(objectName).NewReader(ctx)
}
// Storage is an implementation of the Storage interface, storing keys in Google Cloud Storage buckets.
type Storage struct {
newClient func(ctx context.Context) (gcpStorageAPI, error)
bucketName string
}
// New creates a Storage client for Google Cloud Storage using the provided config.
//
// See the Google docs for more information: https://cloud.google.com/storage/docs/
func New(ctx context.Context, cfg uri.GoogleCloudStorageConfig) (*Storage, error) {
s := &Storage{
newClient: newGCPStorageClientFactory(cfg.CredentialsPath),
bucketName: cfg.Bucket,
}
// Make sure the storage bucket exists, if not create it
if err := s.createContainerOrContinue(ctx, cfg.ProjectID); err != nil {
return nil, err
}
return s, nil
}
// Get returns a DEK from Google Cloud Storage by key ID.
func (s *Storage) Get(ctx context.Context, keyID string) ([]byte, error) {
client, err := s.newClient(ctx)
if err != nil {
return nil, err
}
defer client.Close()
reader, err := client.NewReader(ctx, s.bucketName, keyID)
if err != nil {
if errors.Is(err, gcstorage.ErrObjectNotExist) {
return nil, storage.ErrDEKUnset
}
return nil, err
}
defer reader.Close()
return io.ReadAll(reader)
}
// Put saves a DEK to Google Cloud Storage by key ID.
func (s *Storage) Put(ctx context.Context, keyID string, data []byte) error {
client, err := s.newClient(ctx)
if err != nil {
return err
}
defer client.Close()
writer := client.NewWriter(ctx, s.bucketName, keyID)
defer writer.Close()
_, err = writer.Write(data)
return err
}
func (s *Storage) createContainerOrContinue(ctx context.Context, projectID string) error {
client, err := s.newClient(ctx)
if err != nil {
return err
}
defer client.Close()
if _, err := client.Attrs(ctx, s.bucketName); errors.Is(err, gcstorage.ErrBucketNotExist) {
return client.CreateBucket(ctx, s.bucketName, projectID, nil)
} else if err != nil {
return err
}
return nil
}
func newGCPStorageClientFactory(credPath string) func(context.Context) (gcpStorageAPI, error) {
return func(ctx context.Context) (gcpStorageAPI, error) {
client, err := gcstorage.NewClient(ctx, option.WithCredentialsFile(credPath))
if err != nil {
return nil, err
}
return &wrappedGCPClient{client}, nil
}
}