Skip to content

Commit

Permalink
Switch to Google Cloud API.
Browse files Browse the repository at this point in the history
It allows to specify a path to JSON encoded service account key
or use credentials set through GOOGLE_APPLICATION_CREDENTIALS.
  • Loading branch information
orian committed Jun 12, 2017
1 parent 0f16e17 commit 4e17763
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 150 deletions.
21 changes: 11 additions & 10 deletions google/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"net/url"
"strings"

"cloud.google.com/go/storage"
"github.com/graymeta/stow"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
storage "google.golang.org/api/storage/v1"
"google.golang.org/api/option"
)

// Kind represents the name of the location/storage type.
Expand Down Expand Up @@ -57,20 +57,21 @@ func init() {
}

// Attempts to create a session based on the information given.
func newGoogleStorageClient(config stow.Config) (*storage.Service, error) {
func newGoogleStorageClient(config stow.Config) (*storage.Client, error) {
json, _ := config.Config(ConfigJSON)

scopes := []string{storage.DevstorageReadWriteScope}
scopes := []string{storage.ScopeReadWrite}
if s, ok := config.Config(ConfigScopes); ok && s != "" {
scopes = strings.Split(s, ",")
}

jwtConf, err := google.JWTConfigFromJSON([]byte(json), scopes...)

service, err := storage.New(jwtConf.Client(context.Background()))
if err != nil {
return nil, err
var opts []option.ClientOption
if json != "" {
opts = append(opts, option.WithServiceAccountFile(json))
}
if len(scopes) > 0 {
opts = append(opts, option.WithScopes(scopes...))
}

return service, nil
return storage.NewClient(context.Background(), opts...)
}
143 changes: 47 additions & 96 deletions google/container.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package google

import (
"context"
"io"
"time"

"cloud.google.com/go/storage"
"github.com/graymeta/stow"
"github.com/pkg/errors"
storage "google.golang.org/api/storage/v1"
"google.golang.org/api/iterator"
)

type Container struct {
// Name is needed to retrieve items.
name string

//bucket *storage.BucketHandle

// Client is responsible for performing the requests.
client *storage.Service
client *storage.Client
}

// ID returns a string value which represents the name of the container.
Expand All @@ -27,104 +30,74 @@ func (c *Container) Name() string {
return c.name
}

func (c *Container) Bucket() (*storage.Bucket, error) {
return c.client.Buckets.Get(c.name).Do()
func (c *Container) Bucket() *storage.BucketHandle {
return c.client.Bucket(c.name)
}

// Item returns a stow.Item instance of a container based on the
// name of the container
func (c *Container) Item(id string) (stow.Item, error) {
res, err := c.client.Objects.Get(c.name, id).Do()
if err != nil {
return nil, stow.ErrNotFound
}

t, err := time.Parse(time.RFC3339, res.Updated)
if err != nil {
return nil, err
}

func (c *Container) objectAttrToItem(res *storage.ObjectAttrs) *Item {
u, err := prepUrl(res.MediaLink)
if err != nil {
return nil, err
return nil
}

mdParsed, err := parseMetadata(res.Metadata)
if err != nil {
return nil, err
return nil
}

i := &Item{
name: id,
return &Item{
name: res.Name,
container: c,
client: c.client,
size: int64(res.Size),
etag: res.Etag,
hash: res.Md5Hash,
lastModified: t,
size: res.Size,
etag: "",
hash: string(res.MD5),
lastModified: res.Updated,
url: u,
metadata: mdParsed,
object: res,
}
}

// Item returns a stow.Item instance of a container based on the
// name of the container
func (c *Container) Item(id string) (stow.Item, error) {
obj := c.Bucket().Object(id)
res, err := obj.Attrs(context.TODO())
if err != nil {
return nil, stow.ErrNotFound
}

return i, nil
itm := c.objectAttrToItem(res)
itm.object = obj
return itm, nil
}

// Items retrieves a list of items that are prepended with
// the prefix argument. The 'cursor' variable facilitates pagination.
func (c *Container) Items(prefix string, cursor string, count int) ([]stow.Item, string, error) {
// List all objects in a bucket using pagination
call := c.client.Objects.List(c.name).MaxResults(int64(count))

if prefix != "" {
call.Prefix(prefix)
q := storage.Query{
Prefix: prefix,
}

if cursor != "" {
call = call.PageToken(cursor)
}
pager := iterator.NewPager(c.Bucket().Objects(context.Background(), &q), count, cursor)

res, err := call.Do()
var attrs []*storage.ObjectAttrs
nextPageToken, err := pager.NextPage(&attrs)
if err != nil {
return nil, "", err
}
containerItems := make([]stow.Item, len(res.Items))
containerItems := make([]stow.Item, len(attrs))

for i, o := range res.Items {
t, err := time.Parse(time.RFC3339, o.Updated)
if err != nil {
return nil, "", err
}

u, err := prepUrl(o.MediaLink)
if err != nil {
return nil, "", err
}

mdParsed, err := parseMetadata(o.Metadata)
if err != nil {
return nil, "", err
}

containerItems[i] = &Item{
name: o.Name,
container: c,
client: c.client,
size: int64(o.Size),
etag: o.Etag,
hash: o.Md5Hash,
lastModified: t,
url: u,
metadata: mdParsed,
object: o,
}
for i, o := range attrs {
containerItems[i] = c.objectAttrToItem(o)
}

return containerItems, res.NextPageToken, nil
return containerItems, nextPageToken, nil
}

func (c *Container) RemoveItem(id string) error {
return c.client.Objects.Delete(c.name, id).Do()
return c.Bucket().Object(id).Delete(context.Background())
}

// Put sends a request to upload content to the container. The arguments
Expand All @@ -136,44 +109,22 @@ func (c *Container) Put(name string, r io.Reader, size int64, metadata map[strin
return nil, err
}

object := &storage.Object{
Name: name,
Metadata: mdPrepped,
}
obj := c.Bucket().Object(name)
writer := obj.NewWriter(context.Background())

res, err := c.client.Objects.Insert(c.name, object).Media(r).Do()
if err != nil {
return nil, err
}
writer.Name = name
writer.Metadata = mdPrepped

t, err := time.Parse(time.RFC3339, res.Updated)
_, err = io.Copy(writer, r)
if err != nil {
return nil, err
}

u, err := prepUrl(res.MediaLink)
if err != nil {
return nil, err
}

mdParsed, err := parseMetadata(res.Metadata)
err = writer.Close()
if err != nil {
return nil, err
}

newItem := &Item{
name: name,
container: c,
client: c.client,
size: size,
etag: res.Etag,
hash: res.Md5Hash,
lastModified: t,
url: u,
metadata: mdParsed,
object: res,
}
return newItem, nil
return c.Item(name)
}

func parseMetadata(metadataParsed map[string]string) (map[string]interface{}, error) {
Expand Down
22 changes: 8 additions & 14 deletions google/item.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
package google

import (
"context"
"io"
"net/url"

// "strings"
"time"

storage "google.golang.org/api/storage/v1"
"cloud.google.com/go/storage"
)

type Item struct {
container *Container // Container information is required by a few methods.
client *storage.Service // A client is needed to make requests.
container *Container // Container information is required by a few methods.
client *storage.Client // A client is needed to make requests.
name string
hash string
etag string
size int64
url *url.URL
lastModified time.Time
metadata map[string]interface{}
object *storage.Object
object *storage.ObjectHandle
}

// ID returns a string value that represents the name of a file.
Expand All @@ -45,12 +44,7 @@ func (i *Item) URL() *url.URL {

// Open returns an io.ReadCloser to the object. Useful for downloading/streaming the object.
func (i *Item) Open() (io.ReadCloser, error) {
res, err := i.client.Objects.Get(i.container.name, i.name).Download()
if err != nil {
return nil, err
}

return res.Body, nil
return i.container.Bucket().Object(i.name).NewReader(context.Background())
}

// LastMod returns the last modified date of the item.
Expand All @@ -70,7 +64,7 @@ func (i *Item) ETag() (string, error) {
}

// Object returns the Google Storage Object
func (i *Item) StorageObject() *storage.Object {
func (i *Item) StorageObject() *storage.ObjectHandle {
return i.object
}

Expand All @@ -80,7 +74,7 @@ func prepUrl(str string) (*url.URL, error) {
if err != nil {
return nil, err
}
u.Scheme = "google"
u.Scheme = "gs"

// Discard the query string
u.RawQuery = ""
Expand Down
Loading

0 comments on commit 4e17763

Please sign in to comment.