lib/google/cloud/storage/file.rb in google-cloud-storage-0.23.1 vs lib/google/cloud/storage/file.rb in google-cloud-storage-0.23.2
- old
+ new
@@ -19,10 +19,12 @@
require "google/cloud/storage/file/verifier"
module Google
module Cloud
module Storage
+ GOOGLEAPIS_URL = "https://storage.googleapis.com".freeze
+
##
# # File
#
# Represents a File
# ([Object](https://cloud.google.com/storage/docs/json_api/v1/objects))
@@ -264,10 +266,39 @@
return nil unless @gapi.customer_encryption
Base64.decode64 @gapi.customer_encryption.key_sha256
end
##
+ # The file's storage class. This defines how the file is stored and
+ # determines the SLA and the cost of storage. For more information, see
+ # [Storage
+ # Classes](https://cloud.google.com/storage/docs/storage-classes) and
+ # [Per-Object Storage
+ # Class](https://cloud.google.com/storage/docs/per-object-storage-class).
+ def storage_class
+ @gapi.storage_class
+ end
+
+ ##
+ # Updates how the file is stored and determines the SLA and the cost of
+ # storage. Values include `:multi_regional`, `:regional`, `:nearline`,
+ # `:coldline`, `:standard`, and `:dra` (Durable Reduced Availability),
+ # as well as the strings returned by {File#storage_class} or
+ # {Bucket#storage_class}. For more information, see [Storage
+ # Classes](https://cloud.google.com/storage/docs/storage-classes) and
+ # [Per-Object Storage
+ # Class](https://cloud.google.com/storage/docs/per-object-storage-class).
+ # The default value is the default storage class for the bucket. See
+ # {Bucket#storage_class}.
+ # @param [Symbol, String] storage_class Storage class of the file.
+ def storage_class= storage_class
+ resp = service.update_file_storage_class \
+ bucket, name, storage_class_for(storage_class)
+ @gapi = resp.resource
+ end
+
+ ##
# Updates the file with changes made in the given block in a single
# PATCH request. The following attributes may be set: {#cache_control=},
# {#content_disposition=}, {#content_encoding=}, {#content_language=},
# {#content_type=}, and {#metadata=}. The {#metadata} hash accessible in
# the block is completely mutable and will be included in the request.
@@ -459,10 +490,67 @@
dest_bucket, dest_path, options
File.from_gapi gapi, service
end
##
+ # [Rewrites](https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite)
+ # the file to the same {#bucket} and {#name} with a new
+ # [customer-supplied encryption
+ # key](https://cloud.google.com/storage/docs/encryption#customer-supplied).
+ #
+ # If a new key is provided to this method, the new key must be used to
+ # subsequently download or copy the file. You must securely manage your
+ # keys and ensure that they are not lost. Also, please note that file
+ # metadata is not encrypted, with the exception of the CRC32C checksum
+ # and MD5 hash. The names of files and buckets are also not encrypted,
+ # and you can read or update the metadata of an encrypted file without
+ # providing the encryption key.
+ #
+ # @see https://cloud.google.com/storage/docs/encryption
+ #
+ # @param [String, nil] encryption_key Optional. The last
+ # customer-supplied, AES-256 encryption key used to encrypt the file,
+ # if one was used.
+ # @param [String, nil] new_encryption_key Optional. The new
+ # customer-supplied, AES-256 encryption key with which to encrypt the
+ # file. If `nil`, the rewritten file will be encrypted using the
+ # default server-side encryption, not customer-supplied encryption
+ # keys.
+ #
+ # @return [Google::Cloud::Storage::File]
+ #
+ # @example The file will be rewritten with a new encryption key:
+ # require "google/cloud/storage"
+ #
+ # storage = Google::Cloud::Storage.new
+ # bucket = storage.bucket "my-bucket"
+ #
+ # # Old key was stored securely for later use.
+ # old_key = "y\x03\"\x0E\xB6\xD3\x9B\x0E\xAB*\x19\xFAv\xDEY\xBEI..."
+ #
+ # file = bucket.file "path/to/my-file.ext", encryption_key: old_key
+ #
+ # # Key generation shown for example purposes only. Write your own.
+ # cipher = OpenSSL::Cipher.new "aes-256-cfb"
+ # cipher.encrypt
+ # new_key = cipher.random_key
+ #
+ # file.rotate encryption_key: old_key, new_encryption_key: new_key
+ #
+ def rotate encryption_key: nil, new_encryption_key: nil
+ ensure_service!
+ options = { source_key: encryption_key,
+ destination_key: new_encryption_key }
+ gapi = service.rewrite_file bucket, name, bucket, name, options
+ until gapi.done
+ options[:token] = gapi.rewrite_token
+ gapi = service.rewrite_file bucket, name, bucket, name, options
+ end
+ File.from_gapi gapi.resource, service
+ end
+
+ ##
# Permanently deletes the file.
#
# @return [Boolean] Returns `true` if the file was deleted.
#
# @example
@@ -721,10 +809,22 @@
Verifier.verify_md5! self, file if verify_md5
Verifier.verify_crc32c! self, file if verify_crc32c
file
end
+ def storage_class_for str
+ return nil if str.nil?
+ { "durable_reduced_availability" => "DURABLE_REDUCED_AVAILABILITY",
+ "dra" => "DURABLE_REDUCED_AVAILABILITY",
+ "durable" => "DURABLE_REDUCED_AVAILABILITY",
+ "nearline" => "NEARLINE",
+ "coldline" => "COLDLINE",
+ "multi_regional" => "MULTI_REGIONAL",
+ "regional" => "REGIONAL",
+ "standard" => "STANDARD" }[str.to_s.downcase] || str.to_s
+ end
+
##
# @private Create a signed_url for a file.
class Signer
def initialize bucket, path, service
@bucket = bucket
@@ -747,11 +847,11 @@
end
##
# The external url to the file.
def ext_url
- "https://storage.googleapis.com#{ext_path}"
+ "#{GOOGLEAPIS_URL}#{ext_path}"
end
def apply_option_defaults options
adjusted_expires = (Time.now.utc + (options[:expires] || 300)).to_i
options[:expires] = adjusted_expires
@@ -773,34 +873,61 @@
def determine_issuer options = {}
options[:issuer] || options[:client_email] ||
@service.credentials.issuer
end
+ def post_object options
+ options = apply_option_defaults options
+
+ fields = {
+ key: ext_path.sub("/", "")
+ }
+
+ p = options[:policy] || {}
+ fail "Policy must be given in a Hash" unless p.is_a? Hash
+
+ i = determine_issuer options
+ s = determine_signing_key options
+
+ fail SignedUrlUnavailable unless i && s
+
+ policy_str = p.to_json
+ policy = Base64.strict_encode64(policy_str).delete("\n")
+
+ signature = generate_signature s, policy
+
+ fields[:GoogleAccessId] = i
+ fields[:signature] = signature
+ fields[:policy] = policy
+
+ Google::Cloud::Storage::PostObject.new GOOGLEAPIS_URL, fields
+ end
+
def signed_url options
options = apply_option_defaults options
i = determine_issuer options
s = determine_signing_key options
fail SignedUrlUnavailable unless i && s
- sig = generate_signature s, options
+ sig = generate_signature s, signature_str(options)
generate_signed_url i, sig, options[:expires]
end
- def generate_signature signing_key, options = {}
+ def generate_signature signing_key, secret
unless signing_key.respond_to? :sign
signing_key = OpenSSL::PKey::RSA.new signing_key
end
- signing_key.sign OpenSSL::Digest::SHA256.new, signature_str(options)
+ signature = signing_key.sign OpenSSL::Digest::SHA256.new, secret
+ Base64.strict_encode64(signature).delete("\n")
end
def generate_signed_url issuer, signed_string, expires
- signature = Base64.strict_encode64(signed_string).delete("\n")
"#{ext_url}?GoogleAccessId=#{CGI.escape issuer}" \
"&Expires=#{expires}" \
- "&Signature=#{CGI.escape signature}"
+ "&Signature=#{CGI.escape signed_string}"
end
def format_extension_headers headers
return "" if headers.nil?
fail "Headers must be given in a Hash" unless headers.is_a? Hash
@@ -813,12 +940,18 @@
end
##
# Yielded to a block to accumulate changes for a patch request.
class Updater < File
+ # @private Do not allow storage_class to be set in an update call.
+ # It cannot be set with PATCH.
+ undef :storage_class=
+
+ # @private
attr_reader :updates
+
##
- # Create an Updater object.
+ # @private Create an Updater object.
def initialize gapi
@updates = []
@gapi = gapi
end