storage: support per-object storage class
Fixes #505.
Change-Id: I59ba94369609e4ff8a2982126ca1df738a816aa7
Reviewed-on: https://code-review.googlesource.com/11297
Reviewed-by: Sarah Adams <shadams@google.com>
diff --git a/storage/bucket.go b/storage/bucket.go
index f87fe33..bca1f71 100644
--- a/storage/bucket.go
+++ b/storage/bucket.go
@@ -113,7 +113,7 @@
// MetaGeneration is the metadata generation of the bucket.
MetaGeneration int64
- // StorageClass is the storage class of the bucket. This defines
+ // StorageClass is the default 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 "MULTI_REGIONAL",
// "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
diff --git a/storage/integration_test.go b/storage/integration_test.go
index 6bca383..365ce33 100644
--- a/storage/integration_test.go
+++ b/storage/integration_test.go
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// TODO(jba): use writeObject throughout.
+
package storage
import (
@@ -989,6 +991,67 @@
}
}
+func TestIntegration_PerObjectStorageClass(t *testing.T) {
+ const (
+ defaultStorageClass = "STANDARD"
+ newStorageClass = "MULTI_REGIONAL"
+ )
+ ctx := context.Background()
+ client, bucket := testConfig(ctx, t)
+ defer client.Close()
+
+ bkt := client.Bucket(bucket)
+
+ // The bucket should have the default storage class.
+ battrs, err := bkt.Attrs(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if battrs.StorageClass != defaultStorageClass {
+ t.Fatalf("bucket storage class: got %q, want %q",
+ battrs.StorageClass, defaultStorageClass)
+ }
+ // Write an object; it should start with the bucket's storage class.
+ obj := bkt.Object("posc")
+ if err := writeObject(ctx, obj, "", []byte("foo")); err != nil {
+ t.Fatal(err)
+ }
+ oattrs, err := obj.Attrs(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if oattrs.StorageClass != defaultStorageClass {
+ t.Fatalf("object storage class: got %q, want %q",
+ oattrs.StorageClass, defaultStorageClass)
+ }
+ // Now use Copy to change the storage class.
+ copier := obj.CopierFrom(obj)
+ copier.StorageClass = newStorageClass
+ oattrs2, err := copier.Run(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if oattrs2.StorageClass != newStorageClass {
+ t.Fatalf("new object storage class: got %q, want %q",
+ oattrs2.StorageClass, newStorageClass)
+ }
+
+ // We can also write a new object using a non-default storage class.
+ obj2 := bkt.Object("posc2")
+ w := obj2.NewWriter(ctx)
+ w.StorageClass = newStorageClass
+ if _, err := w.Write([]byte("xxx")); err != nil {
+ t.Fatal(err)
+ }
+ if err := w.Close(); err != nil {
+ t.Fatal(err)
+ }
+ if w.Attrs().StorageClass != newStorageClass {
+ t.Fatalf("new object storage class: got %q, want %q",
+ w.Attrs().StorageClass, newStorageClass)
+ }
+}
+
func TestIntegration_BucketInCopyAttrs(t *testing.T) {
// Confirm that if bucket is included in the object attributes of a rewrite
// call, but object name and content-type aren't, then we get an error. See
@@ -1062,7 +1125,7 @@
return err
}
if time.Since(bktAttrs.Created) > expireAge {
- log.Printf("deleting bucket %q, which more than %s old", bktAttrs.Name, expireAge)
+ log.Printf("deleting bucket %q, which is more than %s old", bktAttrs.Name, expireAge)
if err := killBucket(ctx, client, bktAttrs.Name); err != nil {
return err
}
diff --git a/storage/storage.go b/storage/storage.go
index 53fc029..9d6db94 100644
--- a/storage/storage.go
+++ b/storage/storage.go
@@ -656,6 +656,7 @@
ContentLanguage: o.ContentLanguage,
CacheControl: o.CacheControl,
ContentDisposition: o.ContentDisposition,
+ StorageClass: o.StorageClass,
Acl: acl,
Metadata: o.Metadata,
}
@@ -724,14 +725,13 @@
// of a particular object. This field is read-only.
MetaGeneration int64
- // StorageClass is the storage class of the bucket.
+ // StorageClass is the storage class of the object.
// This value defines how objects in the bucket are stored and
// determines the SLA and the cost of storage. Typical values are
// "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
// and "DURABLE_REDUCED_AVAILABILITY".
// It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
- // or "REGIONAL" depending on the bucket's location settings. This
- // field is read-only.
+ // or "REGIONAL" depending on the bucket's location settings.
StorageClass string
// Created is the time the object was created. This field is read-only.