blob: b415a29428af66f949840da321e1ec6be082fb95 [file] [log] [blame]
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package storage
import (
"encoding/base64"
"io"
"sync"
"time"
"golang.org/x/net/context"
raw "google.golang.org/api/storage/v1"
)
// Bucket represents a Google Cloud Storage bucket.
type Bucket struct {
// Name is the name of the bucket.
Name string
// ACL is the list of access control rules on the bucket.
ACL []ACLRule
// DefaultObjectACL is the list of access controls to
// apply to new objects when no object ACL is provided.
DefaultObjectACL []ACLRule
// Location is the location of the bucket. It defaults to "US".
Location string
// Metageneration is the metadata generation of the bucket.
// Read-only.
Metageneration int64
// StorageClass is the storage class of the bucket. This defines
// how objects in the bucket are stored and determines the SLA
// and the cost of storage. Typical values are "STANDARD" and
// "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD".
StorageClass string
// Created is the creation time of the bucket.
// Read-only.
Created time.Time
}
func newBucket(b *raw.Bucket) *Bucket {
if b == nil {
return nil
}
bucket := &Bucket{
Name: b.Name,
Location: b.Location,
Metageneration: b.Metageneration,
StorageClass: b.StorageClass,
Created: convertTime(b.TimeCreated),
}
acl := make([]ACLRule, len(b.Acl))
for i, rule := range b.Acl {
acl[i] = ACLRule{
Entity: ACLEntity(rule.Entity),
Role: ACLRole(rule.Role),
}
}
bucket.ACL = acl
objACL := make([]ACLRule, len(b.DefaultObjectAcl))
for i, rule := range b.DefaultObjectAcl {
objACL[i] = ACLRule{
Entity: ACLEntity(rule.Entity),
Role: ACLRole(rule.Role),
}
}
bucket.DefaultObjectACL = objACL
return bucket
}
// ObjectAttrs is the user-editable object attributes.
type ObjectAttrs struct {
// Name is the name of the object.
Name string
// ContentType is the MIME type of the object's content.
// Optional.
ContentType string
// ContentLanguage is the optional RFC 1766 Content-Language of
// the object's content sent in response headers.
ContentLanguage string
// ContentEncoding is the optional Content-Encoding of the object
// sent it the response headers.
ContentEncoding string
// CacheControl is the optional Cache-Control header of the object
// sent in the response headers.
CacheControl string
// ACL is the list of access control rules for the object.
// Optional. If nil or empty, existing ACL rules are preserved.
ACL []ACLRule
// Metadata represents user-provided metadata, in key/value pairs.
// It can be nil if the current metadata values needs to preserved.
Metadata map[string]string
}
func (o ObjectAttrs) toRawObject(bucket string) *raw.Object {
var acl []*raw.ObjectAccessControl
if len(o.ACL) > 0 {
acl = make([]*raw.ObjectAccessControl, len(o.ACL))
for i, rule := range o.ACL {
acl[i] = &raw.ObjectAccessControl{
Entity: string(rule.Entity),
Role: string(rule.Role),
}
}
}
return &raw.Object{
Bucket: bucket,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ContentLanguage: o.ContentLanguage,
CacheControl: o.CacheControl,
Acl: acl,
Metadata: o.Metadata,
}
}
// Object represents a Google Cloud Storage (GCS) object.
type Object struct {
// Bucket is the name of the bucket containing this GCS object.
Bucket string
// Name is the name of the object within the bucket.
Name string
// ContentType is the MIME type of the object's content.
ContentType string
// ContentLanguage is the content language of the object's content.
ContentLanguage string
// CacheControl is the Cache-Control header to be sent in the response
// headers when serving the object data.
CacheControl string
// ACL is the list of access control rules for the object.
ACL []ACLRule
// Owner is the owner of the object.
//
// If non-zero, it is in the form of "user-<userId>".
Owner string
// Size is the length of the object's content.
Size int64
// ContentEncoding is the encoding of the object's content.
ContentEncoding string
// MD5 is the MD5 hash of the object's content.
MD5 []byte
// CRC32C is the CRC32 checksum of the object's content using
// the Castagnoli93 polynomial.
CRC32C uint32
// MediaLink is an URL to the object's content.
MediaLink string
// Metadata represents user-provided metadata, in key/value pairs.
// It can be nil if no metadata is provided.
Metadata map[string]string
// Generation is the generation number of the object's content.
Generation int64
// MetaGeneration is the version of the metadata for this
// object at this generation. This field is used for preconditions
// and for detecting changes in metadata. A metageneration number
// is only meaningful in the context of a particular generation
// of a particular object.
MetaGeneration int64
// StorageClass is the storage class of the bucket.
// This value defines how objects in the bucket are stored and
// determines the SLA and the cost of storage. Typical values are
// "STANDARD" and "DURABLE_REDUCED_AVAILABILITY".
// It defaults to "STANDARD".
StorageClass string
// Deleted is the time the object was deleted.
// If not deleted, it is the zero value.
Deleted time.Time
// Updated is the creation or modification time of the object.
// For buckets with versioning enabled, changing an object's
// metadata does not change this property.
Updated time.Time
}
// convertTime converts a time in RFC3339 format to time.Time.
// If any error occurs in parsing, the zero-value time.Time is silently returned.
func convertTime(t string) time.Time {
var r time.Time
if t != "" {
r, _ = time.Parse(time.RFC3339, t)
}
return r
}
func newObject(o *raw.Object) *Object {
if o == nil {
return nil
}
acl := make([]ACLRule, len(o.Acl))
for i, rule := range o.Acl {
acl[i] = ACLRule{
Entity: ACLEntity(rule.Entity),
Role: ACLRole(rule.Role),
}
}
owner := ""
if o.Owner != nil {
owner = o.Owner.Entity
}
md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
var crc32c uint32
d, err := base64.StdEncoding.DecodeString(o.Crc32c)
if err == nil && len(d) == 4 {
crc32c = uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3])
}
return &Object{
Bucket: o.Bucket,
Name: o.Name,
ContentType: o.ContentType,
ContentLanguage: o.ContentLanguage,
CacheControl: o.CacheControl,
ACL: acl,
Owner: owner,
ContentEncoding: o.ContentEncoding,
Size: int64(o.Size),
MD5: md5,
CRC32C: crc32c,
MediaLink: o.MediaLink,
Metadata: o.Metadata,
Generation: o.Generation,
MetaGeneration: o.Metageneration,
StorageClass: o.StorageClass,
Deleted: convertTime(o.TimeDeleted),
Updated: convertTime(o.Updated),
}
}
// Query represents a query to filter objects from a bucket.
type Query struct {
// Delimiter returns results in a directory-like fashion.
// Results will contain only objects whose names, aside from the
// prefix, do not contain delimiter. Objects whose names,
// aside from the prefix, contain delimiter will have their name,
// truncated after the delimiter, returned in prefixes.
// Duplicate prefixes are omitted.
// Optional.
Delimiter string
// Prefix is the prefix filter to query objects
// whose names begin with this prefix.
// Optional.
Prefix string
// Versions indicates whether multiple versions of the same
// object will be included in the results.
Versions bool
// Cursor is a previously-returned page token
// representing part of the larger set of results to view.
// Optional.
Cursor string
// MaxResults is the maximum number of items plus prefixes
// to return. As duplicate prefixes are omitted,
// fewer total results may be returned than requested.
// The default page limit is used if it is negative or zero.
MaxResults int
}
// Objects represents a list of objects returned from
// a bucket look-p request and a query to retrieve more
// objects from the next pages.
type Objects struct {
// Results represent a list of object results.
Results []*Object
// Next is the continuation query to retrieve more
// results with the same filtering criteria. If there
// are no more results to retrieve, it is nil.
Next *Query
// Prefixes represents prefixes of objects
// matching-but-not-listed up to and including
// the requested delimiter.
Prefixes []string
}
// contentTyper implements ContentTyper to enable an
// io.ReadCloser to specify its MIME type.
type contentTyper struct {
io.Reader
t string
}
func (c *contentTyper) ContentType() string {
return c.t
}
// A Writer writes a Cloud Storage object.
type Writer struct {
// ObjectAttrs are optional attributes to set on the object. Any attributes
// must be initialized before the first Write call. Nil or zero-valued
// attributes are ignored.
ObjectAttrs
ctx context.Context
bucket string
name string
once sync.Once
opened bool
r io.Reader
pw *io.PipeWriter
donec chan struct{} // closed after err and obj are set.
err error
obj *Object
}
func (w *Writer) open() {
attrs := w.ObjectAttrs
// Always set the name, otherwise the backend
// rejects the request and responds with an HTTP 400.
if attrs.Name == "" {
attrs.Name = w.name
}
pr, pw := io.Pipe()
w.r = &contentTyper{pr, attrs.ContentType}
w.pw = pw
w.opened = true
go func() {
resp, err := rawService(w.ctx).Objects.Insert(
w.bucket, attrs.toRawObject(w.bucket)).Media(w.r).Projection("full").Do()
w.err = err
if err == nil {
w.obj = newObject(resp)
} else {
pr.CloseWithError(w.err)
}
close(w.donec)
}()
}
// Write appends to w.
func (w *Writer) Write(p []byte) (n int, err error) {
if w.err != nil {
return 0, w.err
}
if !w.opened {
w.open()
}
return w.pw.Write(p)
}
// Close completes the write operation and flushes any buffered data.
// If Close doesn't return an error, metadata about the written object
// can be retrieved by calling Object.
func (w *Writer) Close() error {
if !w.opened {
w.open()
}
if err := w.pw.Close(); err != nil {
return err
}
<-w.donec
return w.err
}
// Object returns metadata about a successfully-written object.
// It's only valid to call it after Close returns nil.
func (w *Writer) Object() *Object {
return w.obj
}