Move to vendor

Signed-off-by: Olivier Gambier <olivier@docker.com>
This commit is contained in:
Olivier Gambier 2016-03-18 14:07:13 -07:00
parent c8d8e7e357
commit 77e69b9cf3
1268 changed files with 34 additions and 24 deletions

176
vendor/google.golang.org/cloud/storage/acl.go generated vendored Normal file
View file

@ -0,0 +1,176 @@
// 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 (
"fmt"
"golang.org/x/net/context"
raw "google.golang.org/api/storage/v1"
)
// ACLRole is the the access permission for the entity.
type ACLRole string
const (
RoleOwner ACLRole = "OWNER"
RoleReader ACLRole = "READER"
)
// ACLEntity is an entity holding an ACL permission.
//
// It could be in the form of:
// "user-<userId>", "user-<email>","group-<groupId>", "group-<email>",
// "domain-<domain>" and "project-team-<projectId>".
//
// Or one of the predefined constants: AllUsers, AllAuthenticatedUsers.
type ACLEntity string
const (
AllUsers ACLEntity = "allUsers"
AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers"
)
// ACLRule represents an access control list rule entry for a Google Cloud Storage object or bucket.
// A bucket is a Google Cloud Storage container whose name is globally unique and contains zero or
// more objects. An object is a blob of data that is stored in a bucket.
type ACLRule struct {
// Entity identifies the entity holding the current rule's permissions.
Entity ACLEntity
// Role is the the access permission for the entity.
Role ACLRole
}
// DefaultACL returns the default object ACL entries for the named bucket.
func DefaultACL(ctx context.Context, bucket string) ([]ACLRule, error) {
acls, err := rawService(ctx).DefaultObjectAccessControls.List(bucket).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("storage: error listing default object ACL for bucket %q: %v", bucket, err)
}
r := make([]ACLRule, 0, len(acls.Items))
for _, v := range acls.Items {
if m, ok := v.(map[string]interface{}); ok {
entity, ok1 := m["entity"].(string)
role, ok2 := m["role"].(string)
if ok1 && ok2 {
r = append(r, ACLRule{Entity: ACLEntity(entity), Role: ACLRole(role)})
}
}
}
return r, nil
}
// PutDefaultACLRule saves the named default object ACL entity with the provided role for the named bucket.
func PutDefaultACLRule(ctx context.Context, bucket string, entity ACLEntity, role ACLRole) error {
acl := &raw.ObjectAccessControl{
Bucket: bucket,
Entity: string(entity),
Role: string(role),
}
_, err := rawService(ctx).DefaultObjectAccessControls.Update(bucket, string(entity), acl).Context(ctx).Do()
if err != nil {
return fmt.Errorf("storage: error updating default ACL rule for bucket %q, entity %q: %v", bucket, entity, err)
}
return nil
}
// DeleteDefaultACLRule deletes the named default ACL entity for the named bucket.
func DeleteDefaultACLRule(ctx context.Context, bucket string, entity ACLEntity) error {
err := rawService(ctx).DefaultObjectAccessControls.Delete(bucket, string(entity)).Context(ctx).Do()
if err != nil {
return fmt.Errorf("storage: error deleting default ACL rule for bucket %q, entity %q: %v", bucket, entity, err)
}
return nil
}
// BucketACL returns the ACL entries for the named bucket.
func BucketACL(ctx context.Context, bucket string) ([]ACLRule, error) {
acls, err := rawService(ctx).BucketAccessControls.List(bucket).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("storage: error listing bucket ACL for bucket %q: %v", bucket, err)
}
r := make([]ACLRule, len(acls.Items))
for i, v := range acls.Items {
r[i].Entity = ACLEntity(v.Entity)
r[i].Role = ACLRole(v.Role)
}
return r, nil
}
// PutBucketACLRule saves the named ACL entity with the provided role for the named bucket.
func PutBucketACLRule(ctx context.Context, bucket string, entity ACLEntity, role ACLRole) error {
acl := &raw.BucketAccessControl{
Bucket: bucket,
Entity: string(entity),
Role: string(role),
}
_, err := rawService(ctx).BucketAccessControls.Update(bucket, string(entity), acl).Context(ctx).Do()
if err != nil {
return fmt.Errorf("storage: error updating bucket ACL rule for bucket %q, entity %q: %v", bucket, entity, err)
}
return nil
}
// DeleteBucketACLRule deletes the named ACL entity for the named bucket.
func DeleteBucketACLRule(ctx context.Context, bucket string, entity ACLEntity) error {
err := rawService(ctx).BucketAccessControls.Delete(bucket, string(entity)).Context(ctx).Do()
if err != nil {
return fmt.Errorf("storage: error deleting bucket ACL rule for bucket %q, entity %q: %v", bucket, entity, err)
}
return nil
}
// ACL returns the ACL entries for the named object.
func ACL(ctx context.Context, bucket, object string) ([]ACLRule, error) {
acls, err := rawService(ctx).ObjectAccessControls.List(bucket, object).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("storage: error listing object ACL for bucket %q, file %q: %v", bucket, object, err)
}
r := make([]ACLRule, 0, len(acls.Items))
for _, v := range acls.Items {
if m, ok := v.(map[string]interface{}); ok {
entity, ok1 := m["entity"].(string)
role, ok2 := m["role"].(string)
if ok1 && ok2 {
r = append(r, ACLRule{Entity: ACLEntity(entity), Role: ACLRole(role)})
}
}
}
return r, nil
}
// PutACLRule saves the named ACL entity with the provided role for the named object.
func PutACLRule(ctx context.Context, bucket, object string, entity ACLEntity, role ACLRole) error {
acl := &raw.ObjectAccessControl{
Bucket: bucket,
Entity: string(entity),
Role: string(role),
}
_, err := rawService(ctx).ObjectAccessControls.Update(bucket, object, string(entity), acl).Context(ctx).Do()
if err != nil {
return fmt.Errorf("storage: error updating object ACL rule for bucket %q, file %q, entity %q: %v", bucket, object, entity, err)
}
return nil
}
// DeleteACLRule deletes the named ACL entity for the named object.
func DeleteACLRule(ctx context.Context, bucket, object string, entity ACLEntity) error {
err := rawService(ctx).ObjectAccessControls.Delete(bucket, object, string(entity)).Context(ctx).Do()
if err != nil {
return fmt.Errorf("storage: error deleting object ACL rule for bucket %q, file %q, entity %q: %v", bucket, object, entity, err)
}
return nil
}

150
vendor/google.golang.org/cloud/storage/example_test.go generated vendored Normal file
View file

@ -0,0 +1,150 @@
// 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_test
import (
"io/ioutil"
"log"
"testing"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/cloud"
"google.golang.org/cloud/storage"
)
// TODO(jbd): Remove after Go 1.4.
// Related to https://codereview.appspot.com/107320046
func TestA(t *testing.T) {}
func Example_auth() context.Context {
// Initialize an authorized context with Google Developers Console
// JSON key. Read the google package examples to learn more about
// different authorization flows you can use.
// http://godoc.org/golang.org/x/oauth2/google
jsonKey, err := ioutil.ReadFile("/path/to/json/keyfile.json")
if err != nil {
log.Fatal(err)
}
conf, err := google.JWTConfigFromJSON(
jsonKey,
storage.ScopeFullControl,
)
if err != nil {
log.Fatal(err)
}
ctx := cloud.NewContext("project-id", conf.Client(oauth2.NoContext))
// Use the context (see other examples)
return ctx
}
func ExampleListObjects() {
ctx := Example_auth()
var query *storage.Query
for {
// If you are using this package on App Engine Managed VMs runtime,
// you can init a bucket client with your app's default bucket name.
// See http://godoc.org/google.golang.org/appengine/file#DefaultBucketName.
objects, err := storage.ListObjects(ctx, "bucketname", query)
if err != nil {
log.Fatal(err)
}
for _, obj := range objects.Results {
log.Printf("object name: %s, size: %v", obj.Name, obj.Size)
}
// if there are more results, objects.Next
// will be non-nil.
query = objects.Next
if query == nil {
break
}
}
log.Println("paginated through all object items in the bucket you specified.")
}
func ExampleNewReader() {
ctx := Example_auth()
rc, err := storage.NewReader(ctx, "bucketname", "filename1")
if err != nil {
log.Fatal(err)
}
slurp, err := ioutil.ReadAll(rc)
rc.Close()
if err != nil {
log.Fatal(err)
}
log.Println("file contents:", slurp)
}
func ExampleNewWriter() {
ctx := Example_auth()
wc := storage.NewWriter(ctx, "bucketname", "filename1")
wc.ContentType = "text/plain"
wc.ACL = []storage.ACLRule{{storage.AllUsers, storage.RoleReader}}
if _, err := wc.Write([]byte("hello world")); err != nil {
log.Fatal(err)
}
if err := wc.Close(); err != nil {
log.Fatal(err)
}
log.Println("updated object:", wc.Object())
}
func ExampleCopyObject() {
ctx := Example_auth()
o, err := storage.CopyObject(ctx, "bucketname", "file1", "another-bucketname", "file2", nil)
if err != nil {
log.Fatal(err)
}
log.Println("copied file:", o)
}
func ExampleDeleteObject() {
// To delete multiple objects in a bucket, first ListObjects then delete them.
ctx := Example_auth()
// If you are using this package on App Engine Managed VMs runtime,
// you can init a bucket client with your app's default bucket name.
// See http://godoc.org/google.golang.org/appengine/file#DefaultBucketName.
const bucket = "bucketname"
var query *storage.Query // Set up query as desired.
for {
objects, err := storage.ListObjects(ctx, bucket, query)
if err != nil {
log.Fatal(err)
}
for _, obj := range objects.Results {
log.Printf("deleting object name: %q, size: %v", obj.Name, obj.Size)
if err := storage.DeleteObject(ctx, bucket, obj.Name); err != nil {
log.Fatalf("unable to delete %q: %v", obj.Name, err)
}
}
// if there are more results, objects.Next will be non-nil.
query = objects.Next
if query == nil {
break
}
}
log.Println("deleted all object items in the bucket you specified.")
}

View file

@ -0,0 +1,327 @@
// 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.
// +build integration
package storage
import (
"bytes"
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"math/rand"
"os"
"testing"
"time"
"google.golang.org/cloud/internal"
"google.golang.org/cloud/internal/testutil"
)
var (
bucket string
contents = make(map[string][]byte)
objects = []string{"obj1", "obj2", "obj/with/slashes"}
aclObjects = []string{"acl1", "acl2"}
copyObj = "copy-object"
)
const envBucket = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
func TestObjects(t *testing.T) {
ctx := testutil.Context(ScopeFullControl)
bucket = os.Getenv(envBucket)
// Cleanup.
cleanup(t, "obj")
const defaultType = "text/plain"
// Test Writer.
for _, obj := range objects {
t.Logf("Writing %v", obj)
wc := NewWriter(ctx, bucket, obj)
wc.ContentType = defaultType
c := randomContents()
if _, err := wc.Write(c); err != nil {
t.Errorf("Write for %v failed with %v", obj, err)
}
if err := wc.Close(); err != nil {
t.Errorf("Close for %v failed with %v", obj, err)
}
contents[obj] = c
}
// Test Reader.
for _, obj := range objects {
t.Logf("Creating a reader to read %v", obj)
rc, err := NewReader(ctx, bucket, obj)
if err != nil {
t.Errorf("Can't create a reader for %v, errored with %v", obj, err)
}
slurp, err := ioutil.ReadAll(rc)
if err != nil {
t.Errorf("Can't ReadAll object %v, errored with %v", obj, err)
}
if got, want := slurp, contents[obj]; !bytes.Equal(got, want) {
t.Errorf("Contents (%v) = %q; want %q", obj, got, want)
}
rc.Close()
// Test SignedURL
opts := &SignedURLOptions{
GoogleAccessID: "xxx@clientid",
PrivateKey: dummyKey("rsa"),
Method: "GET",
MD5: []byte("202cb962ac59075b964b07152d234b70"),
Expires: time.Date(2020, time.October, 2, 10, 0, 0, 0, time.UTC),
ContentType: "application/json",
Headers: []string{"x-header1", "x-header2"},
}
u, err := SignedURL(bucket, obj, opts)
if err != nil {
t.Fatalf("SignedURL(%q, %q) errored with %v", bucket, obj, err)
}
hc := internal.HTTPClient(ctx)
res, err := hc.Get(u)
if err != nil {
t.Fatalf("Can't get URL %q: %v", u, err)
}
slurp, err = ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("Can't ReadAll signed object %v, errored with %v", obj, err)
}
if got, want := slurp, contents[obj]; !bytes.Equal(got, want) {
t.Errorf("Contents (%v) = %q; want %q", obj, got, want)
}
res.Body.Close()
}
// Test NotFound.
_, err := NewReader(ctx, bucket, "obj-not-exists")
if err != ErrObjectNotExist {
t.Errorf("Object should not exist, err found to be %v", err)
}
name := objects[0]
// Test StatObject.
o, err := StatObject(ctx, bucket, name)
if err != nil {
t.Error(err)
}
if got, want := o.Name, name; got != want {
t.Errorf("Name (%v) = %q; want %q", name, got, want)
}
if got, want := o.ContentType, defaultType; got != want {
t.Errorf("ContentType (%v) = %q; want %q", name, got, want)
}
// Test object copy.
copy, err := CopyObject(ctx, bucket, name, bucket, copyObj, nil)
if err != nil {
t.Errorf("CopyObject failed with %v", err)
}
if copy.Name != copyObj {
t.Errorf("Copy object's name = %q; want %q", copy.Name, copyObj)
}
if copy.Bucket != bucket {
t.Errorf("Copy object's bucket = %q; want %q", copy.Bucket, bucket)
}
// Test UpdateAttrs.
updated, err := UpdateAttrs(ctx, bucket, name, ObjectAttrs{
ContentType: "text/html",
ACL: []ACLRule{{Entity: "domain-google.com", Role: RoleReader}},
})
if err != nil {
t.Errorf("UpdateAttrs failed with %v", err)
}
if want := "text/html"; updated.ContentType != want {
t.Errorf("updated.ContentType == %q; want %q", updated.ContentType, want)
}
// Test checksums.
checksumCases := []struct {
name string
contents [][]byte
size int64
md5 string
crc32c uint32
}{
{
name: "checksum-object",
contents: [][]byte{[]byte("hello"), []byte("world")},
size: 10,
md5: "fc5e038d38a57032085441e7fe7010b0",
crc32c: 1456190592,
},
{
name: "zero-object",
contents: [][]byte{},
size: 0,
md5: "d41d8cd98f00b204e9800998ecf8427e",
crc32c: 0,
},
}
for _, c := range checksumCases {
wc := NewWriter(ctx, bucket, c.name)
for _, data := range c.contents {
if _, err := wc.Write(data); err != nil {
t.Errorf("Write(%q) failed with %q", data, err)
}
}
if err = wc.Close(); err != nil {
t.Errorf("%q: close failed with %q", c.name, err)
}
obj := wc.Object()
if got, want := obj.Size, c.size; got != want {
t.Errorf("Object (%q) Size = %v; want %v", c.name, got, want)
}
if got, want := fmt.Sprintf("%x", obj.MD5), c.md5; got != want {
t.Errorf("Object (%q) MD5 = %q; want %q", c.name, got, want)
}
if got, want := obj.CRC32C, c.crc32c; got != want {
t.Errorf("Object (%q) CRC32C = %v; want %v", c.name, got, want)
}
}
// Test public ACL.
publicObj := objects[0]
if err = PutACLRule(ctx, bucket, publicObj, AllUsers, RoleReader); err != nil {
t.Errorf("PutACLRule failed with %v", err)
}
publicCtx := testutil.NoAuthContext()
rc, err := NewReader(publicCtx, bucket, publicObj)
if err != nil {
t.Error(err)
}
slurp, err := ioutil.ReadAll(rc)
if err != nil {
t.Errorf("ReadAll failed with %v", err)
}
if string(slurp) != string(contents[publicObj]) {
t.Errorf("Public object's content is expected to be %s, found %s", contents[publicObj], slurp)
}
rc.Close()
// Test writer error handling.
wc := NewWriter(publicCtx, bucket, publicObj)
if _, err := wc.Write([]byte("hello")); err != nil {
t.Errorf("Write unexpectedly failed with %v", err)
}
if err = wc.Close(); err == nil {
t.Error("Close expected an error, found none")
}
// DeleteObject object.
// The rest of the other object will be deleted during
// the initial cleanup. This tests exists, so we still can cover
// deletion if there are no objects on the bucket to clean.
if err := DeleteObject(ctx, bucket, copyObj); err != nil {
t.Errorf("Deletion of %v failed with %v", copyObj, err)
}
_, err = StatObject(ctx, bucket, copyObj)
if err != ErrObjectNotExist {
t.Errorf("Copy is expected to be deleted, stat errored with %v", err)
}
}
func TestACL(t *testing.T) {
ctx := testutil.Context(ScopeFullControl)
cleanup(t, "acl")
entity := ACLEntity("domain-google.com")
if err := PutDefaultACLRule(ctx, bucket, entity, RoleReader); err != nil {
t.Errorf("Can't put default ACL rule for the bucket, errored with %v", err)
}
for _, obj := range aclObjects {
t.Logf("Writing %v", obj)
wc := NewWriter(ctx, bucket, obj)
c := randomContents()
if _, err := wc.Write(c); err != nil {
t.Errorf("Write for %v failed with %v", obj, err)
}
if err := wc.Close(); err != nil {
t.Errorf("Close for %v failed with %v", obj, err)
}
}
name := aclObjects[0]
acl, err := ACL(ctx, bucket, name)
if err != nil {
t.Errorf("Can't retrieve ACL of %v", name)
}
aclFound := false
for _, rule := range acl {
if rule.Entity == entity && rule.Role == RoleReader {
aclFound = true
}
}
if !aclFound {
t.Error("Expected to find an ACL rule for google.com domain users, but not found")
}
if err := DeleteACLRule(ctx, bucket, name, entity); err != nil {
t.Errorf("Can't delete the ACL rule for the entity: %v", entity)
}
if err := PutBucketACLRule(ctx, bucket, "user-jbd@google.com", RoleReader); err != nil {
t.Errorf("Error while putting bucket ACL rule: %v", err)
}
bACL, err := BucketACL(ctx, bucket)
if err != nil {
t.Errorf("Error while getting the ACL of the bucket: %v", err)
}
bACLFound := false
for _, rule := range bACL {
if rule.Entity == "user-jbd@google.com" && rule.Role == RoleReader {
bACLFound = true
}
}
if !bACLFound {
t.Error("Expected to find an ACL rule for jbd@google.com user, but not found")
}
if err := DeleteBucketACLRule(ctx, bucket, "user-jbd@google.com"); err != nil {
t.Errorf("Error while deleting bucket ACL rule: %v", err)
}
}
func cleanup(t *testing.T, prefix string) {
ctx := testutil.Context(ScopeFullControl)
var q *Query = &Query{
Prefix: prefix,
}
for {
o, err := ListObjects(ctx, bucket, q)
if err != nil {
t.Fatalf("Cleanup List for bucket %v failed with error: %v", bucket, err)
}
for _, obj := range o.Results {
t.Logf("Cleanup deletion of %v", obj.Name)
if err = DeleteObject(ctx, bucket, obj.Name); err != nil {
t.Fatalf("Cleanup Delete for object %v failed with %v", obj.Name, err)
}
}
if o.Next == nil {
break
}
q = o.Next
}
}
func randomContents() []byte {
h := md5.New()
io.WriteString(h, fmt.Sprintf("hello world%d", rand.Intn(100000)))
return h.Sum(nil)
}

350
vendor/google.golang.org/cloud/storage/storage.go generated vendored Normal file
View file

@ -0,0 +1,350 @@
// 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 contains a Google Cloud Storage client.
//
// This package is experimental and may make backwards-incompatible changes.
package storage // import "google.golang.org/cloud/storage"
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"google.golang.org/cloud/internal"
"golang.org/x/net/context"
"google.golang.org/api/googleapi"
raw "google.golang.org/api/storage/v1"
)
var (
ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
ErrObjectNotExist = errors.New("storage: object doesn't exist")
)
const (
// ScopeFullControl grants permissions to manage your
// data and permissions in Google Cloud Storage.
ScopeFullControl = raw.DevstorageFullControlScope
// ScopeReadOnly grants permissions to
// view your data in Google Cloud Storage.
ScopeReadOnly = raw.DevstorageReadOnlyScope
// ScopeReadWrite grants permissions to manage your
// data in Google Cloud Storage.
ScopeReadWrite = raw.DevstorageReadWriteScope
)
// TODO(jbd): Add storage.buckets.list.
// TODO(jbd): Add storage.buckets.insert.
// TODO(jbd): Add storage.buckets.update.
// TODO(jbd): Add storage.buckets.delete.
// TODO(jbd): Add storage.objects.watch.
// BucketInfo returns the metadata for the specified bucket.
func BucketInfo(ctx context.Context, name string) (*Bucket, error) {
resp, err := rawService(ctx).Buckets.Get(name).Projection("full").Context(ctx).Do()
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrBucketNotExist
}
if err != nil {
return nil, err
}
return newBucket(resp), nil
}
// ListObjects lists objects from the bucket. You can specify a query
// to filter the results. If q is nil, no filtering is applied.
func ListObjects(ctx context.Context, bucket string, q *Query) (*Objects, error) {
c := rawService(ctx).Objects.List(bucket)
c.Projection("full")
if q != nil {
c.Delimiter(q.Delimiter)
c.Prefix(q.Prefix)
c.Versions(q.Versions)
c.PageToken(q.Cursor)
if q.MaxResults > 0 {
c.MaxResults(int64(q.MaxResults))
}
}
resp, err := c.Context(ctx).Do()
if err != nil {
return nil, err
}
objects := &Objects{
Results: make([]*Object, len(resp.Items)),
Prefixes: make([]string, len(resp.Prefixes)),
}
for i, item := range resp.Items {
objects.Results[i] = newObject(item)
}
for i, prefix := range resp.Prefixes {
objects.Prefixes[i] = prefix
}
if resp.NextPageToken != "" {
next := Query{}
if q != nil {
// keep the other filtering
// criteria if there is a query
next = *q
}
next.Cursor = resp.NextPageToken
objects.Next = &next
}
return objects, nil
}
// SignedURLOptions allows you to restrict the access to the signed URL.
type SignedURLOptions struct {
// GoogleAccessID represents the authorizer of the signed URL generation.
// It is typically the Google service account client email address from
// the Google Developers Console in the form of "xxx@developer.gserviceaccount.com".
// Required.
GoogleAccessID string
// PrivateKey is the Google service account private key. It is obtainable
// from the Google Developers Console.
// At https://console.developers.google.com/project/<your-project-id>/apiui/credential,
// create a service account client ID or reuse one of your existing service account
// credentials. Click on the "Generate new P12 key" to generate and download
// a new private key. Once you download the P12 file, use the following command
// to convert it into a PEM file.
//
// $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
//
// Provide the contents of the PEM file as a byte slice.
// Required.
PrivateKey []byte
// Method is the HTTP method to be used with the signed URL.
// Signed URLs can be used with GET, HEAD, PUT, and DELETE requests.
// Required.
Method string
// Expires is the expiration time on the signed URL. It must be
// a datetime in the future.
// Required.
Expires time.Time
// ContentType is the content type header the client must provide
// to use the generated signed URL.
// Optional.
ContentType string
// Headers is a list of extention headers the client must provide
// in order to use the generated signed URL.
// Optional.
Headers []string
// MD5 is the base64 encoded MD5 checksum of the file.
// If provided, the client should provide the exact value on the request
// header in order to use the signed URL.
// Optional.
MD5 []byte
}
// SignedURL returns a URL for the specified object. Signed URLs allow
// the users access to a restricted resource for a limited time without having a
// Google account or signing in. For more information about the signed
// URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
if opts == nil {
return "", errors.New("storage: missing required SignedURLOptions")
}
if opts.GoogleAccessID == "" || opts.PrivateKey == nil {
return "", errors.New("storage: missing required credentials to generate a signed URL")
}
if opts.Method == "" {
return "", errors.New("storage: missing required method option")
}
if opts.Expires.IsZero() {
return "", errors.New("storage: missing required expires option")
}
key, err := parseKey(opts.PrivateKey)
if err != nil {
return "", err
}
h := sha256.New()
fmt.Fprintf(h, "%s\n", opts.Method)
fmt.Fprintf(h, "%s\n", opts.MD5)
fmt.Fprintf(h, "%s\n", opts.ContentType)
fmt.Fprintf(h, "%d\n", opts.Expires.Unix())
fmt.Fprintf(h, "%s", strings.Join(opts.Headers, "\n"))
fmt.Fprintf(h, "/%s/%s", bucket, name)
b, err := rsa.SignPKCS1v15(
rand.Reader,
key,
crypto.SHA256,
h.Sum(nil),
)
if err != nil {
return "", err
}
encoded := base64.StdEncoding.EncodeToString(b)
u := &url.URL{
Scheme: "https",
Host: "storage.googleapis.com",
Path: fmt.Sprintf("/%s/%s", bucket, name),
}
q := u.Query()
q.Set("GoogleAccessId", opts.GoogleAccessID)
q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix()))
q.Set("Signature", string(encoded))
u.RawQuery = q.Encode()
return u.String(), nil
}
// StatObject returns meta information about the specified object.
func StatObject(ctx context.Context, bucket, name string) (*Object, error) {
o, err := rawService(ctx).Objects.Get(bucket, name).Projection("full").Context(ctx).Do()
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
}
if err != nil {
return nil, err
}
return newObject(o), nil
}
// UpdateAttrs updates an object with the provided attributes.
// All zero-value attributes are ignored.
func UpdateAttrs(ctx context.Context, bucket, name string, attrs ObjectAttrs) (*Object, error) {
o, err := rawService(ctx).Objects.Patch(bucket, name, attrs.toRawObject(bucket)).Projection("full").Context(ctx).Do()
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
}
if err != nil {
return nil, err
}
return newObject(o), nil
}
// DeleteObject deletes the single specified object.
func DeleteObject(ctx context.Context, bucket, name string) error {
return rawService(ctx).Objects.Delete(bucket, name).Context(ctx).Do()
}
// CopyObject copies the source object to the destination.
// The copied object's attributes are overwritten by attrs if non-nil.
func CopyObject(ctx context.Context, srcBucket, srcName string, destBucket, destName string, attrs *ObjectAttrs) (*Object, error) {
if srcBucket == "" || destBucket == "" {
return nil, errors.New("storage: srcBucket and destBucket must both be non-empty")
}
if srcName == "" || destName == "" {
return nil, errors.New("storage: srcName and destName must be non-empty")
}
var rawObject *raw.Object
if attrs != nil {
attrs.Name = destName
if attrs.ContentType == "" {
return nil, errors.New("storage: attrs.ContentType must be non-empty")
}
rawObject = attrs.toRawObject(destBucket)
}
o, err := rawService(ctx).Objects.Copy(
srcBucket, srcName, destBucket, destName, rawObject).Projection("full").Context(ctx).Do()
if err != nil {
return nil, err
}
return newObject(o), nil
}
// NewReader creates a new io.ReadCloser to read the contents
// of the object.
func NewReader(ctx context.Context, bucket, name string) (io.ReadCloser, error) {
hc := internal.HTTPClient(ctx)
u := &url.URL{
Scheme: "https",
Host: "storage.googleapis.com",
Path: fmt.Sprintf("/%s/%s", bucket, name),
}
res, err := hc.Get(u.String())
if err != nil {
return nil, err
}
if res.StatusCode == http.StatusNotFound {
res.Body.Close()
return nil, ErrObjectNotExist
}
if res.StatusCode < 200 || res.StatusCode > 299 {
res.Body.Close()
return res.Body, fmt.Errorf("storage: can't read object %v/%v, status code: %v", bucket, name, res.Status)
}
return res.Body, nil
}
// NewWriter returns a storage Writer that writes to the GCS object
// identified by the specified name.
// If such an object doesn't exist, it creates one.
// Attributes can be set on the object by modifying the returned Writer's
// ObjectAttrs field before the first call to Write. The name parameter to this
// function is ignored if the Name field of the ObjectAttrs field is set to a
// non-empty string.
//
// It is the caller's responsibility to call Close when writing is done.
//
// The object is not available and any previous object with the same
// name is not replaced on Cloud Storage until Close is called.
func NewWriter(ctx context.Context, bucket, name string) *Writer {
return &Writer{
ctx: ctx,
bucket: bucket,
name: name,
donec: make(chan struct{}),
}
}
func rawService(ctx context.Context) *raw.Service {
return internal.Service(ctx, "storage", func(hc *http.Client) interface{} {
svc, _ := raw.New(hc)
return svc
}).(*raw.Service)
}
// parseKey converts the binary contents of a private key file
// to an *rsa.PrivateKey. It detects whether the private key is in a
// PEM container or not. If so, it extracts the the private key
// from PEM container before conversion. It only supports PEM
// containers with no passphrase.
func parseKey(key []byte) (*rsa.PrivateKey, error) {
if block, _ := pem.Decode(key); block != nil {
key = block.Bytes
}
parsedKey, err := x509.ParsePKCS8PrivateKey(key)
if err != nil {
parsedKey, err = x509.ParsePKCS1PrivateKey(key)
if err != nil {
return nil, err
}
}
parsed, ok := parsedKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("oauth2: private key is invalid")
}
return parsed, nil
}

238
vendor/google.golang.org/cloud/storage/storage_test.go generated vendored Normal file
View file

@ -0,0 +1,238 @@
// 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 (
"fmt"
"io/ioutil"
"log"
"strings"
"testing"
"time"
"golang.org/x/net/context"
)
func TestSignedURL(t *testing.T) {
expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00")
url, err := SignedURL("bucket-name", "object-name", &SignedURLOptions{
GoogleAccessID: "xxx@clientid",
PrivateKey: dummyKey("rsa"),
Method: "GET",
MD5: []byte("202cb962ac59075b964b07152d234b70"),
Expires: expires,
ContentType: "application/json",
Headers: []string{"x-header1", "x-header2"},
})
if err != nil {
t.Error(err)
}
want := "https://storage.googleapis.com/bucket-name/object-name?" +
"Expires=1033570800&GoogleAccessId=xxx%40clientid&Signature=" +
"ITqNWQHr7ayIj%2B0Ds5%2FzUT2cWMQQouuFmu6L11Zd3kfNKvm3sjyGIzO" +
"gZsSUoter1SxP7BcrCzgqIZ9fQmgQnuIpqqLL4kcGmTbKsQS6hTknpJM%2F" +
"2lS4NY6UH1VXBgm2Tce28kz8rnmqG6svcGvtWuOgJsETeSIl1R9nAEIDCEq" +
"ZJzoOiru%2BODkHHkpoFjHWAwHugFHX%2B9EX4SxaytiN3oEy48HpYGWV0I" +
"h8NvU1hmeWzcLr41GnTADeCn7Eg%2Fb5H2GCNO70Cz%2Bw2fn%2BofLCUeR" +
"YQd%2FhES8oocv5kpHZkstc8s8uz3aKMsMauzZ9MOmGy%2F6VULBgIVvi6a" +
"AwEBIYOw%3D%3D"
if url != want {
t.Fatalf("Unexpected signed URL; found %v", url)
}
}
func TestSignedURL_PEMPrivateKey(t *testing.T) {
expires, _ := time.Parse(time.RFC3339, "2002-10-02T10:00:00-05:00")
url, err := SignedURL("bucket-name", "object-name", &SignedURLOptions{
GoogleAccessID: "xxx@clientid",
PrivateKey: dummyKey("pem"),
Method: "GET",
MD5: []byte("202cb962ac59075b964b07152d234b70"),
Expires: expires,
ContentType: "application/json",
Headers: []string{"x-header1", "x-header2"},
})
if err != nil {
t.Error(err)
}
want := "https://storage.googleapis.com/bucket-name/object-name?" +
"Expires=1033570800&GoogleAccessId=xxx%40clientid&Signature=" +
"B7XkS4dfmVDoe%2FoDeXZkWlYmg8u2kI0SizTrzL5%2B9RmKnb5j7Kf34DZ" +
"JL8Hcjr1MdPFLNg2QV4lEH86Gqgqt%2Fv3jFOTRl4wlzcRU%2FvV5c5HU8M" +
"qW0FZ0IDbqod2RdsMONLEO6yQWV2HWFrMLKl2yMFlWCJ47et%2BFaHe6v4Z" +
"EBc0%3D"
if url != want {
t.Fatalf("Unexpected signed URL; found %v", url)
}
}
func TestSignedURL_MissingOptions(t *testing.T) {
pk := dummyKey("rsa")
var tests = []struct {
opts *SignedURLOptions
errMsg string
}{
{
&SignedURLOptions{},
"missing required credentials",
},
{
&SignedURLOptions{GoogleAccessID: "access_id"},
"missing required credentials",
},
{
&SignedURLOptions{
GoogleAccessID: "access_id",
PrivateKey: pk,
},
"missing required method",
},
{
&SignedURLOptions{
GoogleAccessID: "access_id",
PrivateKey: pk,
Method: "PUT",
},
"missing required expires",
},
}
for _, test := range tests {
_, err := SignedURL("bucket", "name", test.opts)
if !strings.Contains(err.Error(), test.errMsg) {
t.Errorf("expected err: %v, found: %v", test.errMsg, err)
}
}
}
func dummyKey(kind string) []byte {
slurp, err := ioutil.ReadFile(fmt.Sprintf("./testdata/dummy_%s", kind))
if err != nil {
log.Fatal(err)
}
return slurp
}
func TestCopyObjectMissingFields(t *testing.T) {
var tests = []struct {
srcBucket, srcName, destBucket, destName string
errMsg string
}{
{
"mybucket", "", "mybucket", "destname",
"srcName and destName must be non-empty",
},
{
"mybucket", "srcname", "mybucket", "",
"srcName and destName must be non-empty",
},
{
"", "srcfile", "mybucket", "destname",
"srcBucket and destBucket must both be non-empty",
},
{
"mybucket", "srcfile", "", "destname",
"srcBucket and destBucket must both be non-empty",
},
}
for i, test := range tests {
_, err := CopyObject(context.TODO(), test.srcBucket, test.srcName, test.destBucket, test.destName, nil)
if !strings.Contains(err.Error(), test.errMsg) {
t.Errorf("CopyObject test #%v: err = %v, want %v", i, err, test.errMsg)
}
}
}
func TestObjectNames(t *testing.T) {
// Naming requirements: https://cloud.google.com/storage/docs/bucket-naming
const maxLegalLength = 1024
type testT struct {
name, want string
}
tests := []testT{
// Embedded characters important in URLs.
{"foo % bar", "foo%20%25%20bar"},
{"foo ? bar", "foo%20%3F%20bar"},
{"foo / bar", "foo%20/%20bar"},
{"foo %?/ bar", "foo%20%25%3F/%20bar"},
// Non-Roman scripts
{"타코", "%ED%83%80%EC%BD%94"},
{"世界", "%E4%B8%96%E7%95%8C"},
// Longest legal name
{strings.Repeat("a", maxLegalLength), strings.Repeat("a", maxLegalLength)},
// Line terminators besides CR and LF: https://en.wikipedia.org/wiki/Newline#Unicode
{"foo \u000b bar", "foo%20%0B%20bar"},
{"foo \u000c bar", "foo%20%0C%20bar"},
{"foo \u0085 bar", "foo%20%C2%85%20bar"},
{"foo \u2028 bar", "foo%20%E2%80%A8%20bar"},
{"foo \u2029 bar", "foo%20%E2%80%A9%20bar"},
// Null byte.
{"foo \u0000 bar", "foo%20%00%20bar"},
// Non-control characters that are discouraged, but not forbidden, according to the documentation.
{"foo # bar", "foo%20%23%20bar"},
{"foo []*? bar", "foo%20%5B%5D%2A%3F%20bar"},
// Angstrom symbol singleton and normalized forms: http://unicode.org/reports/tr15/
{"foo \u212b bar", "foo%20%E2%84%AB%20bar"},
{"foo \u0041\u030a bar", "foo%20A%CC%8A%20bar"},
{"foo \u00c5 bar", "foo%20%C3%85%20bar"},
// Hangul separating jamo: http://www.unicode.org/versions/Unicode7.0.0/ch18.pdf (Table 18-10)
{"foo \u3131\u314f bar", "foo%20%E3%84%B1%E3%85%8F%20bar"},
{"foo \u1100\u1161 bar", "foo%20%E1%84%80%E1%85%A1%20bar"},
{"foo \uac00 bar", "foo%20%EA%B0%80%20bar"},
}
// C0 control characters not forbidden by the docs.
var runes []rune
for r := rune(0x01); r <= rune(0x1f); r++ {
if r != '\u000a' && r != '\u000d' {
runes = append(runes, r)
}
}
tests = append(tests, testT{fmt.Sprintf("foo %s bar", string(runes)), "foo%20%01%02%03%04%05%06%07%08%09%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20bar"})
// C1 control characters, plus DEL.
runes = nil
for r := rune(0x7f); r <= rune(0x9f); r++ {
runes = append(runes, r)
}
tests = append(tests, testT{fmt.Sprintf("foo %s bar", string(runes)), "foo%20%7F%C2%80%C2%81%C2%82%C2%83%C2%84%C2%85%C2%86%C2%87%C2%88%C2%89%C2%8A%C2%8B%C2%8C%C2%8D%C2%8E%C2%8F%C2%90%C2%91%C2%92%C2%93%C2%94%C2%95%C2%96%C2%97%C2%98%C2%99%C2%9A%C2%9B%C2%9C%C2%9D%C2%9E%C2%9F%20bar"})
opts := &SignedURLOptions{
GoogleAccessID: "xxx@clientid",
PrivateKey: dummyKey("rsa"),
Method: "GET",
MD5: []byte("202cb962ac59075b964b07152d234b70"),
Expires: time.Date(2002, time.October, 2, 10, 0, 0, 0, time.UTC),
ContentType: "application/json",
Headers: []string{"x-header1", "x-header2"},
}
for _, test := range tests {
g, err := SignedURL("bucket-name", test.name, opts)
if err != nil {
t.Errorf("SignedURL(%q) err=%v, want nil", test.name, err)
}
if w := "/bucket-name/" + test.want; !strings.Contains(g, w) {
t.Errorf("SignedURL(%q)=%q, want substring %q", test.name, g, w)
}
}
}

View file

@ -0,0 +1,39 @@
Bag Attributes
friendlyName: privatekey
localKeyID: 54 69 6D 65 20 31 34 31 36 38 35 32 30 30 34 37 37 32
Key Attributes: <No Attributes>
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCtCWMoJ2Bok2QoGFyU7A6IlGprO9QfUTT0jNrLkIbM5OWNIuDx
64+PEaTS5g5m+2Hz/lmd5jJKanAH4dY9LZzsaYAPq1K17Gcmg1hEisYeKsgOcjYY
kwRkV+natCTsC+tfWmS0voRh0jA1rI1J4MikceoHtgWdEuoHrrptRVpWKwIDAQAB
AoGAKp3uQvx3vSnX+BwP6Um+RpsvHpwMoW3xue1bEdnVqW8SrlERz+NxZw40ZxDs
KSbuuBZD4iTI7BUM5JQVnNm4FQY1YrPlWZLyI73Bj8RKTXrPdJheM/0r7xjiIXbQ
7w4cUSM9rVugnI/rxF2kPIQTGYI+EG/6+P+k6VvgPmC0T/ECQQDUPskiS18WaY+i
Koalbrb3GakaBoHrC1b4ln4CAv7fq7H4WvFvqi/2rxLhHYq31iwxYy8s7J7Sba1+
5vwJ2TxZAkEA0LVfs3Q2VWZ+cM3bv0aYTalMXg6wT+LoNvk9HnOb0zQYajF3qm4G
ZFdfEqvOkje0zQ4fcihARKyda/VY84UGIwJBAIZa0FvjNmgrnn7bSKzEbxHwrnkJ
EYjGfuGR8mY3mzvfpiM+/oLfSslvfhX+62cALq18yco4ZzlxsFgaxAU//NECQDcS
NN94YcHlGqYPW9W7/gI4EwOaoqFhwV6II71+SfbP/0U+KlJZV+xwNZEKrqZcdqPI
/zkzL8ovNha/laokRrsCQQCyoPHGcBWj+VFbNoyQnX4tghc6rOY7n4pmpgQvU825
TAM9vnYtSkKK/V56kEDNBO5LwiRsir95IUNclqqMKR1C
-----END RSA PRIVATE KEY-----
Bag Attributes
friendlyName: privatekey
localKeyID: 54 69 6D 65 20 31 34 31 36 38 35 32 30 30 34 37 37 32
subject=/CN=1079432350659-nvog0vmn9s6pqr3kr4v2avbc7nkhoa11.apps.googleusercontent.com
issuer=/CN=1079432350659-nvog0vmn9s6pqr3kr4v2avbc7nkhoa11.apps.googleusercontent.com
-----BEGIN CERTIFICATE-----
MIICXTCCAcagAwIBAgIIHxTMQUVJRZ0wDQYJKoZIhvcNAQEFBQAwVDFSMFAGA1UE
AxNJMTA3OTQzMjM1MDY1OS1udm9nMHZtbjlzNnBxcjNrcjR2MmF2YmM3bmtob2Ex
MS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTAeFw0xNDExMjQxODAwMDRaFw0y
NDExMjExODAwMDRaMFQxUjBQBgNVBAMTSTEwNzk0MzIzNTA2NTktbnZvZzB2bW45
czZwcXIza3I0djJhdmJjN25raG9hMTEuYXBwcy5nb29nbGV1c2VyY29udGVudC5j
b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK0JYygnYGiTZCgYXJTsDoiU
ams71B9RNPSM2suQhszk5Y0i4PHrj48RpNLmDmb7YfP+WZ3mMkpqcAfh1j0tnOxp
gA+rUrXsZyaDWESKxh4qyA5yNhiTBGRX6dq0JOwL619aZLS+hGHSMDWsjUngyKRx
6ge2BZ0S6geuum1FWlYrAgMBAAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/
BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GB
ACVvKkZkomHq3uffOQwdZ4VJYuxrvDGnZu/ExW9WngO2teEsjxABL41TNnRYHN5T
lMC19poFA2tR/DySDLJ2XNs/hSvyQUL6HHCncVdR4Srpie88j48peY1MZSMP51Jv
qagbbP5K5DSEu02/zZaV0kaCvLEN0KAtj/noDuOOnQU2
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE
DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY
fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK
1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr
k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9
/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt
3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn
2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3
nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK
6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf
5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e
DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1
M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g
z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y
1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK
J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U
f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx
QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA
cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr
Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw
5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg
KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84
OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd
mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ
5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg==
-----END RSA PRIVATE KEY-----

417
vendor/google.golang.org/cloud/storage/types.go generated vendored Normal file
View file

@ -0,0 +1,417 @@
// 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
// ContentDisposition is the optional Content-Disposition header of the object
// sent in the response headers.
ContentDisposition 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,
ContentDisposition: o.ContentDisposition,
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").Context(w.ctx).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
}
// CloseWithError aborts the write operation with the provided error.
// CloseWithError always returns nil.
func (w *Writer) CloseWithError(err error) error {
if !w.opened {
return nil
}
return w.pw.CloseWithError(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
}

42
vendor/google.golang.org/cloud/storage/types_test.go generated vendored Normal file
View file

@ -0,0 +1,42 @@
// 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 (
"fmt"
"net/http"
"testing"
"google.golang.org/cloud"
)
type fakeTransport struct{}
func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return nil, fmt.Errorf("error handling request")
}
func TestErrorOnObjectsInsertCall(t *testing.T) {
ctx := cloud.NewContext("project-id", &http.Client{
Transport: &fakeTransport{}})
wc := NewWriter(ctx, "bucketname", "filename1")
wc.ContentType = "text/plain"
if _, err := wc.Write([]byte("hello world")); err == nil {
t.Errorf("expected error on write, got nil")
}
if err := wc.Close(); err == nil {
t.Errorf("expected error on close, got nil")
}
}