storage.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package storage contains a Google Cloud Storage client.
  15. //
  16. // This package is experimental and may make backwards-incompatible changes.
  17. package storage // import "google.golang.org/cloud/storage"
  18. import (
  19. "crypto"
  20. "crypto/rand"
  21. "crypto/rsa"
  22. "crypto/sha256"
  23. "crypto/x509"
  24. "encoding/base64"
  25. "encoding/pem"
  26. "errors"
  27. "fmt"
  28. "net/http"
  29. "net/url"
  30. "strings"
  31. "time"
  32. "unicode/utf8"
  33. "google.golang.org/cloud"
  34. "google.golang.org/cloud/internal/transport"
  35. "golang.org/x/net/context"
  36. "google.golang.org/api/googleapi"
  37. raw "google.golang.org/api/storage/v1"
  38. )
  39. var (
  40. ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
  41. ErrObjectNotExist = errors.New("storage: object doesn't exist")
  42. )
  43. const userAgent = "gcloud-golang-storage/20151204"
  44. const (
  45. // ScopeFullControl grants permissions to manage your
  46. // data and permissions in Google Cloud Storage.
  47. ScopeFullControl = raw.DevstorageFullControlScope
  48. // ScopeReadOnly grants permissions to
  49. // view your data in Google Cloud Storage.
  50. ScopeReadOnly = raw.DevstorageReadOnlyScope
  51. // ScopeReadWrite grants permissions to manage your
  52. // data in Google Cloud Storage.
  53. ScopeReadWrite = raw.DevstorageReadWriteScope
  54. )
  55. // AdminClient is a client type for performing admin operations on a project's
  56. // buckets.
  57. type AdminClient struct {
  58. hc *http.Client
  59. raw *raw.Service
  60. projectID string
  61. }
  62. // NewAdminClient creates a new AdminClient for a given project.
  63. func NewAdminClient(ctx context.Context, projectID string, opts ...cloud.ClientOption) (*AdminClient, error) {
  64. c, err := NewClient(ctx, opts...)
  65. if err != nil {
  66. return nil, err
  67. }
  68. return &AdminClient{
  69. hc: c.hc,
  70. raw: c.raw,
  71. projectID: projectID,
  72. }, nil
  73. }
  74. // Close closes the AdminClient.
  75. func (c *AdminClient) Close() error {
  76. c.hc = nil
  77. return nil
  78. }
  79. // Create creates a Bucket in the project.
  80. // If attrs is nil the API defaults will be used.
  81. func (c *AdminClient) CreateBucket(ctx context.Context, bucketName string, attrs *BucketAttrs) error {
  82. var bkt *raw.Bucket
  83. if attrs != nil {
  84. bkt = attrs.toRawBucket()
  85. } else {
  86. bkt = &raw.Bucket{}
  87. }
  88. bkt.Name = bucketName
  89. req := c.raw.Buckets.Insert(c.projectID, bkt)
  90. _, err := req.Context(ctx).Do()
  91. return err
  92. }
  93. // Delete deletes a Bucket in the project.
  94. func (c *AdminClient) DeleteBucket(ctx context.Context, bucketName string) error {
  95. req := c.raw.Buckets.Delete(bucketName)
  96. return req.Context(ctx).Do()
  97. }
  98. // Client is a client for interacting with Google Cloud Storage.
  99. type Client struct {
  100. hc *http.Client
  101. raw *raw.Service
  102. }
  103. // NewClient creates a new Google Cloud Storage client.
  104. // The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use cloud.WithScopes.
  105. func NewClient(ctx context.Context, opts ...cloud.ClientOption) (*Client, error) {
  106. o := []cloud.ClientOption{
  107. cloud.WithScopes(ScopeFullControl),
  108. cloud.WithUserAgent(userAgent),
  109. }
  110. opts = append(o, opts...)
  111. hc, _, err := transport.NewHTTPClient(ctx, opts...)
  112. if err != nil {
  113. return nil, fmt.Errorf("dialing: %v", err)
  114. }
  115. rawService, err := raw.New(hc)
  116. if err != nil {
  117. return nil, fmt.Errorf("storage client: %v", err)
  118. }
  119. return &Client{
  120. hc: hc,
  121. raw: rawService,
  122. }, nil
  123. }
  124. // Close closes the Client.
  125. func (c *Client) Close() error {
  126. c.hc = nil
  127. return nil
  128. }
  129. // BucketHandle provides operations on a Google Cloud Storage bucket.
  130. // Use Client.Bucket to get a handle.
  131. type BucketHandle struct {
  132. acl *ACLHandle
  133. defaultObjectACL *ACLHandle
  134. c *Client
  135. name string
  136. }
  137. // Bucket returns a BucketHandle, which provides operations on the named bucket.
  138. // This call does not perform any network operations.
  139. //
  140. // name must contain only lowercase letters, numbers, dashes, underscores, and
  141. // dots. The full specification for valid bucket names can be found at:
  142. // https://cloud.google.com/storage/docs/bucket-naming
  143. func (c *Client) Bucket(name string) *BucketHandle {
  144. return &BucketHandle{
  145. c: c,
  146. name: name,
  147. acl: &ACLHandle{
  148. c: c,
  149. bucket: name,
  150. },
  151. defaultObjectACL: &ACLHandle{
  152. c: c,
  153. bucket: name,
  154. isDefault: true,
  155. },
  156. }
  157. }
  158. // ACL returns an ACLHandle, which provides access to the bucket's access control list.
  159. // This controls who can list, create or overwrite the objects in a bucket.
  160. // This call does not perform any network operations.
  161. func (c *BucketHandle) ACL() *ACLHandle {
  162. return c.acl
  163. }
  164. // DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs.
  165. // These ACLs are applied to newly created objects in this bucket that do not have a defined ACL.
  166. // This call does not perform any network operations.
  167. func (c *BucketHandle) DefaultObjectACL() *ACLHandle {
  168. return c.defaultObjectACL
  169. }
  170. // ObjectHandle provides operations on an object in a Google Cloud Storage bucket.
  171. // Use BucketHandle.Object to get a handle.
  172. type ObjectHandle struct {
  173. c *Client
  174. bucket string
  175. object string
  176. acl *ACLHandle
  177. }
  178. // Object returns an ObjectHandle, which provides operations on the named object.
  179. // This call does not perform any network operations.
  180. //
  181. // name must consist entirely of valid UTF-8-encoded runes. The full specification
  182. // for valid object names can be found at:
  183. // https://cloud.google.com/storage/docs/bucket-naming
  184. func (b *BucketHandle) Object(name string) *ObjectHandle {
  185. return &ObjectHandle{
  186. c: b.c,
  187. bucket: b.name,
  188. object: name,
  189. acl: &ACLHandle{
  190. c: b.c,
  191. bucket: b.name,
  192. object: name,
  193. },
  194. }
  195. }
  196. // TODO(jbd): Add storage.buckets.list.
  197. // TODO(jbd): Add storage.buckets.update.
  198. // TODO(jbd): Add storage.objects.watch.
  199. // Attrs returns the metadata for the bucket.
  200. func (b *BucketHandle) Attrs(ctx context.Context) (*BucketAttrs, error) {
  201. resp, err := b.c.raw.Buckets.Get(b.name).Projection("full").Context(ctx).Do()
  202. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  203. return nil, ErrBucketNotExist
  204. }
  205. if err != nil {
  206. return nil, err
  207. }
  208. return newBucket(resp), nil
  209. }
  210. // List lists objects from the bucket. You can specify a query
  211. // to filter the results. If q is nil, no filtering is applied.
  212. func (b *BucketHandle) List(ctx context.Context, q *Query) (*ObjectList, error) {
  213. req := b.c.raw.Objects.List(b.name)
  214. req.Projection("full")
  215. if q != nil {
  216. req.Delimiter(q.Delimiter)
  217. req.Prefix(q.Prefix)
  218. req.Versions(q.Versions)
  219. req.PageToken(q.Cursor)
  220. if q.MaxResults > 0 {
  221. req.MaxResults(int64(q.MaxResults))
  222. }
  223. }
  224. resp, err := req.Context(ctx).Do()
  225. if err != nil {
  226. return nil, err
  227. }
  228. objects := &ObjectList{
  229. Results: make([]*ObjectAttrs, len(resp.Items)),
  230. Prefixes: make([]string, len(resp.Prefixes)),
  231. }
  232. for i, item := range resp.Items {
  233. objects.Results[i] = newObject(item)
  234. }
  235. for i, prefix := range resp.Prefixes {
  236. objects.Prefixes[i] = prefix
  237. }
  238. if resp.NextPageToken != "" {
  239. next := Query{}
  240. if q != nil {
  241. // keep the other filtering
  242. // criteria if there is a query
  243. next = *q
  244. }
  245. next.Cursor = resp.NextPageToken
  246. objects.Next = &next
  247. }
  248. return objects, nil
  249. }
  250. // SignedURLOptions allows you to restrict the access to the signed URL.
  251. type SignedURLOptions struct {
  252. // GoogleAccessID represents the authorizer of the signed URL generation.
  253. // It is typically the Google service account client email address from
  254. // the Google Developers Console in the form of "xxx@developer.gserviceaccount.com".
  255. // Required.
  256. GoogleAccessID string
  257. // PrivateKey is the Google service account private key. It is obtainable
  258. // from the Google Developers Console.
  259. // At https://console.developers.google.com/project/<your-project-id>/apiui/credential,
  260. // create a service account client ID or reuse one of your existing service account
  261. // credentials. Click on the "Generate new P12 key" to generate and download
  262. // a new private key. Once you download the P12 file, use the following command
  263. // to convert it into a PEM file.
  264. //
  265. // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
  266. //
  267. // Provide the contents of the PEM file as a byte slice.
  268. // Required.
  269. PrivateKey []byte
  270. // Method is the HTTP method to be used with the signed URL.
  271. // Signed URLs can be used with GET, HEAD, PUT, and DELETE requests.
  272. // Required.
  273. Method string
  274. // Expires is the expiration time on the signed URL. It must be
  275. // a datetime in the future.
  276. // Required.
  277. Expires time.Time
  278. // ContentType is the content type header the client must provide
  279. // to use the generated signed URL.
  280. // Optional.
  281. ContentType string
  282. // Headers is a list of extention headers the client must provide
  283. // in order to use the generated signed URL.
  284. // Optional.
  285. Headers []string
  286. // MD5 is the base64 encoded MD5 checksum of the file.
  287. // If provided, the client should provide the exact value on the request
  288. // header in order to use the signed URL.
  289. // Optional.
  290. MD5 []byte
  291. }
  292. // SignedURL returns a URL for the specified object. Signed URLs allow
  293. // the users access to a restricted resource for a limited time without having a
  294. // Google account or signing in. For more information about the signed
  295. // URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
  296. func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
  297. if opts == nil {
  298. return "", errors.New("storage: missing required SignedURLOptions")
  299. }
  300. if opts.GoogleAccessID == "" || opts.PrivateKey == nil {
  301. return "", errors.New("storage: missing required credentials to generate a signed URL")
  302. }
  303. if opts.Method == "" {
  304. return "", errors.New("storage: missing required method option")
  305. }
  306. if opts.Expires.IsZero() {
  307. return "", errors.New("storage: missing required expires option")
  308. }
  309. key, err := parseKey(opts.PrivateKey)
  310. if err != nil {
  311. return "", err
  312. }
  313. h := sha256.New()
  314. fmt.Fprintf(h, "%s\n", opts.Method)
  315. fmt.Fprintf(h, "%s\n", opts.MD5)
  316. fmt.Fprintf(h, "%s\n", opts.ContentType)
  317. fmt.Fprintf(h, "%d\n", opts.Expires.Unix())
  318. fmt.Fprintf(h, "%s", strings.Join(opts.Headers, "\n"))
  319. fmt.Fprintf(h, "/%s/%s", bucket, name)
  320. b, err := rsa.SignPKCS1v15(
  321. rand.Reader,
  322. key,
  323. crypto.SHA256,
  324. h.Sum(nil),
  325. )
  326. if err != nil {
  327. return "", err
  328. }
  329. encoded := base64.StdEncoding.EncodeToString(b)
  330. u := &url.URL{
  331. Scheme: "https",
  332. Host: "storage.googleapis.com",
  333. Path: fmt.Sprintf("/%s/%s", bucket, name),
  334. }
  335. q := u.Query()
  336. q.Set("GoogleAccessId", opts.GoogleAccessID)
  337. q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix()))
  338. q.Set("Signature", string(encoded))
  339. u.RawQuery = q.Encode()
  340. return u.String(), nil
  341. }
  342. // ACL provides access to the object's access control list.
  343. // This controls who can read and write this object.
  344. // This call does not perform any network operations.
  345. func (o *ObjectHandle) ACL() *ACLHandle {
  346. return o.acl
  347. }
  348. // Attrs returns meta information about the object.
  349. // ErrObjectNotExist will be returned if the object is not found.
  350. func (o *ObjectHandle) Attrs(ctx context.Context) (*ObjectAttrs, error) {
  351. if !utf8.ValidString(o.object) {
  352. return nil, fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
  353. }
  354. obj, err := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx).Do()
  355. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  356. return nil, ErrObjectNotExist
  357. }
  358. if err != nil {
  359. return nil, err
  360. }
  361. return newObject(obj), nil
  362. }
  363. // Update updates an object with the provided attributes.
  364. // All zero-value attributes are ignored.
  365. // ErrObjectNotExist will be returned if the object is not found.
  366. func (o *ObjectHandle) Update(ctx context.Context, attrs ObjectAttrs) (*ObjectAttrs, error) {
  367. if !utf8.ValidString(o.object) {
  368. return nil, fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
  369. }
  370. obj, err := o.c.raw.Objects.Patch(o.bucket, o.object, attrs.toRawObject(o.bucket)).Projection("full").Context(ctx).Do()
  371. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  372. return nil, ErrObjectNotExist
  373. }
  374. if err != nil {
  375. return nil, err
  376. }
  377. return newObject(obj), nil
  378. }
  379. // Delete deletes the single specified object.
  380. func (o *ObjectHandle) Delete(ctx context.Context) error {
  381. if !utf8.ValidString(o.object) {
  382. return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
  383. }
  384. return o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx).Do()
  385. }
  386. // CopyObject copies the source object to the destination.
  387. // The copied object's attributes are overwritten by attrs if non-nil.
  388. func (c *Client) CopyObject(ctx context.Context, srcBucket, srcName string, destBucket, destName string, attrs *ObjectAttrs) (*ObjectAttrs, error) {
  389. if srcBucket == "" || destBucket == "" {
  390. return nil, errors.New("storage: srcBucket and destBucket must both be non-empty")
  391. }
  392. if srcName == "" || destName == "" {
  393. return nil, errors.New("storage: srcName and destName must be non-empty")
  394. }
  395. if !utf8.ValidString(srcName) {
  396. return nil, fmt.Errorf("storage: srcName %q is not valid UTF-8", srcName)
  397. }
  398. if !utf8.ValidString(destName) {
  399. return nil, fmt.Errorf("storage: destName %q is not valid UTF-8", destName)
  400. }
  401. var rawObject *raw.Object
  402. if attrs != nil {
  403. attrs.Name = destName
  404. if attrs.ContentType == "" {
  405. return nil, errors.New("storage: attrs.ContentType must be non-empty")
  406. }
  407. rawObject = attrs.toRawObject(destBucket)
  408. }
  409. o, err := c.raw.Objects.Copy(
  410. srcBucket, srcName, destBucket, destName, rawObject).Projection("full").Context(ctx).Do()
  411. if err != nil {
  412. return nil, err
  413. }
  414. return newObject(o), nil
  415. }
  416. // NewReader creates a new Reader to read the contents of the
  417. // object.
  418. // ErrObjectNotExist will be returned if the object is not found.
  419. func (o *ObjectHandle) NewReader(ctx context.Context) (*Reader, error) {
  420. if !utf8.ValidString(o.object) {
  421. return nil, fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
  422. }
  423. u := &url.URL{
  424. Scheme: "https",
  425. Host: "storage.googleapis.com",
  426. Path: fmt.Sprintf("/%s/%s", o.bucket, o.object),
  427. }
  428. res, err := o.c.hc.Get(u.String())
  429. if err != nil {
  430. return nil, err
  431. }
  432. if res.StatusCode == http.StatusNotFound {
  433. res.Body.Close()
  434. return nil, ErrObjectNotExist
  435. }
  436. if res.StatusCode < 200 || res.StatusCode > 299 {
  437. res.Body.Close()
  438. return nil, fmt.Errorf("storage: can't read object %v/%v, status code: %v", o.bucket, o.object, res.Status)
  439. }
  440. return &Reader{
  441. body: res.Body,
  442. size: res.ContentLength,
  443. contentType: res.Header.Get("Content-Type"),
  444. }, nil
  445. }
  446. // NewWriter returns a storage Writer that writes to the GCS object
  447. // associated with this ObjectHandle.
  448. // If such an object doesn't exist, it creates one.
  449. // Attributes can be set on the object by modifying the returned Writer's
  450. // ObjectAttrs field before the first call to Write.
  451. //
  452. // It is the caller's responsibility to call Close when writing is done.
  453. //
  454. // The object is not available and any previous object with the same
  455. // name is not replaced on Cloud Storage until Close is called.
  456. func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer {
  457. return &Writer{
  458. ctx: ctx,
  459. client: o.c,
  460. bucket: o.bucket,
  461. name: o.object,
  462. donec: make(chan struct{}),
  463. ObjectAttrs: ObjectAttrs{Name: o.object},
  464. }
  465. }
  466. // parseKey converts the binary contents of a private key file
  467. // to an *rsa.PrivateKey. It detects whether the private key is in a
  468. // PEM container or not. If so, it extracts the the private key
  469. // from PEM container before conversion. It only supports PEM
  470. // containers with no passphrase.
  471. func parseKey(key []byte) (*rsa.PrivateKey, error) {
  472. if block, _ := pem.Decode(key); block != nil {
  473. key = block.Bytes
  474. }
  475. parsedKey, err := x509.ParsePKCS8PrivateKey(key)
  476. if err != nil {
  477. parsedKey, err = x509.ParsePKCS1PrivateKey(key)
  478. if err != nil {
  479. return nil, err
  480. }
  481. }
  482. parsed, ok := parsedKey.(*rsa.PrivateKey)
  483. if !ok {
  484. return nil, errors.New("oauth2: private key is invalid")
  485. }
  486. return parsed, nil
  487. }