| package gcsclient |
| |
| import ( |
| "context" |
| "fmt" |
| "io" |
| |
| "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 io.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) 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++ |
| if err := callback(obj); err != nil { |
| return err |
| } |
| } |
| 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) |