blob: dd705281dad04ee13497e26696a09d25c3c9718e [file] [log] [blame]
package gcsclient
import (
"context"
"fmt"
"io"
"io/ioutil"
"cloud.google.com/go/storage"
"go.skia.org/infra/go/gcs"
"go.skia.org/infra/go/util"
"google.golang.org/api/iterator"
)
// TODO(dogben, kjlubick): This should really have some tests.
// StorageClient holds the information needed to talk to cloud storage
// and fulfill the gcs.GCSClient interface
type StorageClient struct {
client *storage.Client
bucket string
}
// New returns a new *StorageClient. See the gcs.GCSClient interface for more information.
func New(s *storage.Client, bucket string) *StorageClient {
return &StorageClient{
client: s,
bucket: bucket,
}
}
// See the GCSClient interface for more information about FileReader.
func (g *StorageClient) FileReader(ctx context.Context, path string) (io.ReadCloser, error) {
// TODO(dogben): if reader.Attrs.ContentEncoding == "gzip" then we should use ReadCompressed here
// to get the compressed content, and wrap the reader in a gzip.Reader. Currently, with NewReader,
// the content is decompressed on the server side; using ReadCompressed + gzip.Reader would save
// bandwidth when retrieving while preserving the current behaviour.
return g.client.Bucket(g.bucket).Object(path).NewReader(ctx)
}
// See the GCSClient interface for more information about FileWriter.
func (g *StorageClient) FileWriter(ctx context.Context, path string, opts gcs.FileWriteOptions) io.WriteCloser {
w := g.client.Bucket(g.bucket).Object(path).NewWriter(ctx)
w.ObjectAttrs.ContentEncoding = opts.ContentEncoding
w.ObjectAttrs.ContentType = opts.ContentType
w.ObjectAttrs.ContentLanguage = opts.ContentLanguage
w.ObjectAttrs.ContentDisposition = opts.ContentDisposition
w.ObjectAttrs.Metadata = opts.Metadata
return w
}
// See the GCSClient interface for more information about DoesFileExist.
func (g *StorageClient) DoesFileExist(ctx context.Context, path string) (bool, error) {
if _, err := g.client.Bucket(g.bucket).Object(path).Attrs(ctx); err != nil {
if err == storage.ErrObjectNotExist {
return false, nil
}
return false, err
}
return true, nil
}
// See the GCSClient interface for more information about GetFileContents.
func (g *StorageClient) GetFileContents(ctx context.Context, path string) ([]byte, error) {
response, err := g.client.Bucket(g.bucket).Object(path).NewReader(ctx)
if err != nil {
return nil, err
}
defer util.Close(response)
return ioutil.ReadAll(response)
}
// See the GCSClient interface for more information about SetFileContents.
func (g *StorageClient) SetFileContents(ctx context.Context, path string, opts gcs.FileWriteOptions, contents []byte) (rv error) {
return gcs.WithWriteFile(g, ctx, path, opts, func(w io.Writer) error {
_, err := w.Write(contents)
return err
})
}
// See the GCSClient interface for more information about GetFileObjectAttrs.
func (g *StorageClient) GetFileObjectAttrs(ctx context.Context, path string) (*storage.ObjectAttrs, error) {
return g.client.Bucket(g.bucket).Object(path).Attrs(ctx)
}
// See the GCSClient interface for more information about AllFilesInDirectory.
func (g *StorageClient) AllFilesInDirectory(ctx context.Context, prefix string, callback func(item *storage.ObjectAttrs)) error {
total := 0
q := &storage.Query{Prefix: prefix, Versions: false}
it := g.client.Bucket(g.bucket).Objects(ctx, q)
for obj, err := it.Next(); err != iterator.Done; obj, err = it.Next() {
if err != nil {
return fmt.Errorf("Problem reading from Google Storage: %v", err)
}
total++
callback(obj)
}
return nil
}
// See the GCSClient interface for more information about DeleteFile.
func (g *StorageClient) DeleteFile(ctx context.Context, path string) error {
return g.client.Bucket(g.bucket).Object(path).Delete(ctx)
}
// See the GCSClient interface for more information about Bucket.
func (g *StorageClient) Bucket() string {
return g.bucket
}
var _ gcs.GCSClient = (*StorageClient)(nil)