google-cloud-storage 1.18.1 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AUTHENTICATION.md +17 -30
- data/CHANGELOG.md +312 -0
- data/CONTRIBUTING.md +4 -5
- data/LOGGING.md +1 -1
- data/OVERVIEW.md +37 -5
- data/TROUBLESHOOTING.md +2 -8
- data/lib/google/cloud/storage/bucket/acl.rb +40 -40
- data/lib/google/cloud/storage/bucket/cors.rb +4 -1
- data/lib/google/cloud/storage/bucket/lifecycle.rb +259 -44
- data/lib/google/cloud/storage/bucket/list.rb +3 -3
- data/lib/google/cloud/storage/bucket.rb +1096 -172
- data/lib/google/cloud/storage/convert.rb +4 -3
- data/lib/google/cloud/storage/credentials.rb +16 -14
- data/lib/google/cloud/storage/errors.rb +7 -2
- data/lib/google/cloud/storage/file/acl.rb +181 -20
- data/lib/google/cloud/storage/file/list.rb +10 -8
- data/lib/google/cloud/storage/file/signer_v2.rb +36 -18
- data/lib/google/cloud/storage/file/signer_v4.rb +249 -61
- data/lib/google/cloud/storage/file/verifier.rb +2 -2
- data/lib/google/cloud/storage/file.rb +450 -84
- data/lib/google/cloud/storage/hmac_key/list.rb +182 -0
- data/lib/google/cloud/storage/hmac_key.rb +316 -0
- data/lib/google/cloud/storage/policy/binding.rb +246 -0
- data/lib/google/cloud/storage/policy/bindings.rb +196 -0
- data/lib/google/cloud/storage/policy/condition.rb +138 -0
- data/lib/google/cloud/storage/policy.rb +277 -24
- data/lib/google/cloud/storage/post_object.rb +20 -2
- data/lib/google/cloud/storage/project.rb +249 -50
- data/lib/google/cloud/storage/service.rb +479 -288
- data/lib/google/cloud/storage/version.rb +1 -1
- data/lib/google/cloud/storage.rb +86 -16
- data/lib/google-cloud-storage.rb +54 -7
- metadata +74 -27
@@ -191,7 +191,7 @@ module Google
|
|
191
191
|
# @return [Integer]
|
192
192
|
#
|
193
193
|
def size
|
194
|
-
@gapi.size
|
194
|
+
@gapi.size&.to_i
|
195
195
|
end
|
196
196
|
|
197
197
|
##
|
@@ -260,6 +260,9 @@ module Google
|
|
260
260
|
# directive for the file data. If omitted, and the file is accessible
|
261
261
|
# to all anonymous users, the default will be `public, max-age=3600`.
|
262
262
|
#
|
263
|
+
# To pass generation and/or metageneration preconditions, call this
|
264
|
+
# method within a block passed to {#update}.
|
265
|
+
#
|
263
266
|
# @param [String] cache_control The Cache-Control directive.
|
264
267
|
#
|
265
268
|
def cache_control= cache_control
|
@@ -281,6 +284,9 @@ module Google
|
|
281
284
|
# Updates the [Content-Disposition](https://tools.ietf.org/html/rfc6266)
|
282
285
|
# of the file data.
|
283
286
|
#
|
287
|
+
# To pass generation and/or metageneration preconditions, call this
|
288
|
+
# method within a block passed to {#update}.
|
289
|
+
#
|
284
290
|
# @param [String] content_disposition The Content-Disposition of the
|
285
291
|
# file.
|
286
292
|
#
|
@@ -305,6 +311,9 @@ module Google
|
|
305
311
|
# ](https://tools.ietf.org/html/rfc7231#section-3.1.2.2) of the file
|
306
312
|
# data.
|
307
313
|
#
|
314
|
+
# To pass generation and/or metageneration preconditions, call this
|
315
|
+
# method within a block passed to {#update}.
|
316
|
+
#
|
308
317
|
# @param [String] content_encoding The Content-Encoding of the file.
|
309
318
|
#
|
310
319
|
def content_encoding= content_encoding
|
@@ -326,6 +335,9 @@ module Google
|
|
326
335
|
# Updates the [Content-Language](http://tools.ietf.org/html/bcp47) of
|
327
336
|
# the file data.
|
328
337
|
#
|
338
|
+
# To pass generation and/or metageneration preconditions, call this
|
339
|
+
# method within a block passed to {#update}.
|
340
|
+
#
|
329
341
|
# @param [String] content_language The Content-Language of the file.
|
330
342
|
#
|
331
343
|
def content_language= content_language
|
@@ -348,6 +360,9 @@ module Google
|
|
348
360
|
# [Content-Type](https://tools.ietf.org/html/rfc2616#section-14.17) of
|
349
361
|
# the file data.
|
350
362
|
#
|
363
|
+
# To pass generation and/or metageneration preconditions, call this
|
364
|
+
# method within a block passed to {#update}.
|
365
|
+
#
|
351
366
|
# @param [String] content_type The Content-Type of the file.
|
352
367
|
#
|
353
368
|
def content_type= content_type
|
@@ -355,6 +370,32 @@ module Google
|
|
355
370
|
update_gapi! :content_type
|
356
371
|
end
|
357
372
|
|
373
|
+
##
|
374
|
+
# A custom time specified by the user for the file, or `nil`.
|
375
|
+
#
|
376
|
+
# @return [DateTime, nil]
|
377
|
+
#
|
378
|
+
def custom_time
|
379
|
+
@gapi.custom_time
|
380
|
+
end
|
381
|
+
|
382
|
+
##
|
383
|
+
# Updates the custom time specified by the user for the file. Once set,
|
384
|
+
# custom_time can't be unset, and it can only be changed to a time in the
|
385
|
+
# future. If custom_time must be unset, you must either perform a rewrite
|
386
|
+
# operation, or upload the data again and create a new file.
|
387
|
+
#
|
388
|
+
# To pass generation and/or metageneration preconditions, call this
|
389
|
+
# method within a block passed to {#update}.
|
390
|
+
#
|
391
|
+
# @param [DateTime] custom_time A custom time specified by the user
|
392
|
+
# for the file.
|
393
|
+
#
|
394
|
+
def custom_time= custom_time
|
395
|
+
@gapi.custom_time = custom_time
|
396
|
+
update_gapi! :custom_time
|
397
|
+
end
|
398
|
+
|
358
399
|
##
|
359
400
|
# A hash of custom, user-provided web-safe keys and arbitrary string
|
360
401
|
# values that will returned with requests for the file as "x-goog-meta-"
|
@@ -373,6 +414,9 @@ module Google
|
|
373
414
|
# string values that will returned with requests for the file as
|
374
415
|
# "x-goog-meta-" response headers.
|
375
416
|
#
|
417
|
+
# To pass generation and/or metageneration preconditions, call this
|
418
|
+
# method within a block passed to {#update}.
|
419
|
+
#
|
376
420
|
# @param [Hash(String => String)] metadata The user-provided metadata,
|
377
421
|
# in key/value pairs.
|
378
422
|
#
|
@@ -389,7 +433,8 @@ module Google
|
|
389
433
|
# You can use this SHA256 hash to uniquely identify the AES-256
|
390
434
|
# encryption key required to decrypt this file.
|
391
435
|
#
|
392
|
-
# @return [String]
|
436
|
+
# @return [String, nil] The encoded SHA256 hash, or `nil` if there is
|
437
|
+
# no customer-supplied encryption key for this file.
|
393
438
|
#
|
394
439
|
def encryption_key_sha256
|
395
440
|
return nil unless @gapi.customer_encryption
|
@@ -428,10 +473,10 @@ module Google
|
|
428
473
|
# Rewrites the file with a new storage class, which determines the SLA
|
429
474
|
# and the cost of storage. Accepted values include:
|
430
475
|
#
|
431
|
-
# * `:
|
432
|
-
# * `:regional`
|
476
|
+
# * `:standard`
|
433
477
|
# * `:nearline`
|
434
478
|
# * `:coldline`
|
479
|
+
# * `:archive`
|
435
480
|
#
|
436
481
|
# as well as the equivalent strings returned by {File#storage_class} or
|
437
482
|
# {Bucket#storage_class}. For more information, see [Storage
|
@@ -441,6 +486,9 @@ module Google
|
|
441
486
|
# The default value is the default storage class for the bucket. See
|
442
487
|
# {Bucket#storage_class}.
|
443
488
|
#
|
489
|
+
# To pass generation and/or metageneration preconditions, call this
|
490
|
+
# method within a block passed to {#update}.
|
491
|
+
#
|
444
492
|
# @param [Symbol, String] storage_class Storage class of the file.
|
445
493
|
#
|
446
494
|
def storage_class= storage_class
|
@@ -481,6 +529,9 @@ module Google
|
|
481
529
|
# removed, the file's `retention_expires_at` date is not changed. The
|
482
530
|
# default value is `false`.
|
483
531
|
#
|
532
|
+
# To pass generation and/or metageneration preconditions, call this
|
533
|
+
# method within a block passed to {#update}.
|
534
|
+
#
|
484
535
|
# See {#retention_expires_at}.
|
485
536
|
#
|
486
537
|
# @example
|
@@ -509,6 +560,9 @@ module Google
|
|
509
560
|
#
|
510
561
|
# See {#retention_expires_at}.
|
511
562
|
#
|
563
|
+
# To pass generation and/or metageneration preconditions, call this
|
564
|
+
# method within a block passed to {#update}.
|
565
|
+
#
|
512
566
|
# @example
|
513
567
|
# require "google/cloud/storage"
|
514
568
|
#
|
@@ -619,6 +673,9 @@ module Google
|
|
619
673
|
# holds released prior to the effective date of the new policy may
|
620
674
|
# have already been deleted by the user.
|
621
675
|
#
|
676
|
+
# To pass generation and/or metageneration preconditions, call this
|
677
|
+
# method within a block passed to {#update}.
|
678
|
+
#
|
622
679
|
# @example
|
623
680
|
# require "google/cloud/storage"
|
624
681
|
#
|
@@ -657,6 +714,9 @@ module Google
|
|
657
714
|
# {Bucket#default_event_based_hold?} and
|
658
715
|
# {Bucket#default_event_based_hold=}.
|
659
716
|
#
|
717
|
+
# To pass generation and/or metageneration preconditions, call this
|
718
|
+
# method within a block passed to {#update}.
|
719
|
+
#
|
660
720
|
# @example
|
661
721
|
# require "google/cloud/storage"
|
662
722
|
#
|
@@ -746,8 +806,25 @@ module Google
|
|
746
806
|
# Updates the file with changes made in the given block in a single
|
747
807
|
# PATCH request. The following attributes may be set: {#cache_control=},
|
748
808
|
# {#content_disposition=}, {#content_encoding=}, {#content_language=},
|
749
|
-
# {#content_type=}, and {#metadata=}. The {#metadata} hash
|
750
|
-
# the block is completely mutable and will be included in the
|
809
|
+
# {#content_type=}, {#custom_time=} and {#metadata=}. The {#metadata} hash
|
810
|
+
# accessible in the block is completely mutable and will be included in the
|
811
|
+
# request.
|
812
|
+
#
|
813
|
+
# @param [Integer] generation Select a specific revision of the file to
|
814
|
+
# update. The default is the latest version.
|
815
|
+
# @param [Integer] if_generation_match Makes the operation conditional
|
816
|
+
# on whether the file's current generation matches the given value.
|
817
|
+
# Setting to 0 makes the operation succeed only if there are no live
|
818
|
+
# versions of the file.
|
819
|
+
# @param [Integer] if_generation_not_match Makes the operation conditional
|
820
|
+
# on whether the file's current generation does not match the given
|
821
|
+
# value. If no live file exists, the precondition fails. Setting to 0
|
822
|
+
# makes the operation succeed only if there is a live version of the file.
|
823
|
+
# @param [Integer] if_metageneration_match Makes the operation conditional
|
824
|
+
# on whether the file's current metageneration matches the given value.
|
825
|
+
# @param [Integer] if_metageneration_not_match Makes the operation
|
826
|
+
# conditional on whether the file's current metageneration does not
|
827
|
+
# match the given value.
|
751
828
|
#
|
752
829
|
# @yield [file] a block yielding a delegate object for updating the file
|
753
830
|
#
|
@@ -766,19 +843,43 @@ module Google
|
|
766
843
|
# f.content_encoding = "deflate"
|
767
844
|
# f.content_language = "de"
|
768
845
|
# f.content_type = "application/json"
|
846
|
+
# f.custom_time = DateTime.new 2025, 12, 31
|
769
847
|
# f.metadata["player"] = "Bob"
|
770
848
|
# f.metadata["score"] = "10"
|
771
849
|
# end
|
772
850
|
#
|
773
|
-
|
851
|
+
# @example With a `if_generation_match` precondition:
|
852
|
+
# require "google/cloud/storage"
|
853
|
+
#
|
854
|
+
# storage = Google::Cloud::Storage.new
|
855
|
+
#
|
856
|
+
# bucket = storage.bucket "my-bucket"
|
857
|
+
#
|
858
|
+
# file = bucket.file "path/to/my-file.ext"
|
859
|
+
#
|
860
|
+
# file.update if_generation_match: 1602263125261858 do |f|
|
861
|
+
# f.cache_control = "private, max-age=0, no-cache"
|
862
|
+
# end
|
863
|
+
#
|
864
|
+
def update generation: nil,
|
865
|
+
if_generation_match: nil,
|
866
|
+
if_generation_not_match: nil,
|
867
|
+
if_metageneration_match: nil,
|
868
|
+
if_metageneration_not_match: nil
|
774
869
|
updater = Updater.new gapi
|
775
870
|
yield updater
|
776
871
|
updater.check_for_changed_metadata!
|
777
|
-
|
872
|
+
return if updater.updates.empty?
|
873
|
+
update_gapi! updater.updates,
|
874
|
+
generation: generation,
|
875
|
+
if_generation_match: if_generation_match,
|
876
|
+
if_generation_not_match: if_generation_not_match,
|
877
|
+
if_metageneration_match: if_metageneration_match,
|
878
|
+
if_metageneration_not_match: if_metageneration_not_match
|
778
879
|
end
|
779
880
|
|
780
881
|
##
|
781
|
-
#
|
882
|
+
# Downloads the file's contents to a local file or an File-like object.
|
782
883
|
#
|
783
884
|
# By default, the download is verified by calculating the MD5 digest.
|
784
885
|
#
|
@@ -938,8 +1039,8 @@ module Google
|
|
938
1039
|
end
|
939
1040
|
file, resp =
|
940
1041
|
service.download_file bucket, name, path,
|
941
|
-
|
942
|
-
user_project: user_project
|
1042
|
+
generation: generation, key: encryption_key,
|
1043
|
+
range: range, user_project: user_project
|
943
1044
|
# FIX: downloading with encryption key will return nil
|
944
1045
|
file ||= ::File.new path
|
945
1046
|
verify = :none if range
|
@@ -952,7 +1053,15 @@ module Google
|
|
952
1053
|
end
|
953
1054
|
|
954
1055
|
##
|
955
|
-
#
|
1056
|
+
# Copies the file to a new location. Metadata excluding ACL from the source
|
1057
|
+
# object will be copied to the destination object unless a block is provided.
|
1058
|
+
#
|
1059
|
+
# If an optional block for updating is provided, only the updates made in
|
1060
|
+
# this block will appear in the destination object, and other metadata
|
1061
|
+
# fields in the destination object will not be copied. To copy the other
|
1062
|
+
# source file metadata fields while updating destination fields in a
|
1063
|
+
# block, use the `force_copy_metadata: true` flag, and the client library
|
1064
|
+
# will copy metadata from source metadata into the copy request.
|
956
1065
|
#
|
957
1066
|
# If a [customer-supplied encryption
|
958
1067
|
# key](https://cloud.google.com/storage/docs/encryption#customer-supplied)
|
@@ -987,6 +1096,19 @@ module Google
|
|
987
1096
|
# @param [String] encryption_key Optional. The customer-supplied,
|
988
1097
|
# AES-256 encryption key used to encrypt the file, if one was provided
|
989
1098
|
# to {Bucket#create_file}.
|
1099
|
+
# @param [Boolean] force_copy_metadata Optional. If `true` and if updates
|
1100
|
+
# are made in a block, the following fields will be copied from the
|
1101
|
+
# source file to the destination file (except when changed by updates):
|
1102
|
+
#
|
1103
|
+
# * `cache_control`
|
1104
|
+
# * `content_disposition`
|
1105
|
+
# * `content_encoding`
|
1106
|
+
# * `content_language`
|
1107
|
+
# * `content_type`
|
1108
|
+
# * `metadata`
|
1109
|
+
#
|
1110
|
+
# If `nil` or `false`, only the updates made in the yielded block will
|
1111
|
+
# be applied to the destination object. The default is `nil`.
|
990
1112
|
# @yield [file] a block yielding a delegate object for updating
|
991
1113
|
#
|
992
1114
|
# @return [Google::Cloud::Storage::File]
|
@@ -1036,12 +1158,13 @@ module Google
|
|
1036
1158
|
# f.metadata["copied_from"] = "#{file.bucket}/#{file.name}"
|
1037
1159
|
# end
|
1038
1160
|
#
|
1039
|
-
def copy dest_bucket_or_path, dest_path = nil,
|
1040
|
-
|
1161
|
+
def copy dest_bucket_or_path, dest_path = nil, acl: nil, generation: nil, encryption_key: nil,
|
1162
|
+
force_copy_metadata: nil
|
1041
1163
|
rewrite dest_bucket_or_path, dest_path,
|
1042
1164
|
acl: acl, generation: generation,
|
1043
1165
|
encryption_key: encryption_key,
|
1044
|
-
new_encryption_key: encryption_key
|
1166
|
+
new_encryption_key: encryption_key,
|
1167
|
+
force_copy_metadata: force_copy_metadata do |updater|
|
1045
1168
|
yield updater if block_given?
|
1046
1169
|
end
|
1047
1170
|
end
|
@@ -1049,7 +1172,15 @@ module Google
|
|
1049
1172
|
##
|
1050
1173
|
# [Rewrites](https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite)
|
1051
1174
|
# the file to a new location. Or the same location can be provided to
|
1052
|
-
# rewrite the file in place.
|
1175
|
+
# rewrite the file in place. Metadata from the source object will
|
1176
|
+
# be copied to the destination object unless a block is provided.
|
1177
|
+
#
|
1178
|
+
# If an optional block for updating is provided, only the updates made in
|
1179
|
+
# this block will appear in the destination object, and other metadata
|
1180
|
+
# fields in the destination object will not be copied. To copy the other
|
1181
|
+
# source file metadata fields while updating destination fields in a
|
1182
|
+
# block, use the `force_copy_metadata: true` flag, and the client library
|
1183
|
+
# will copy metadata from source metadata into the copy request.
|
1053
1184
|
#
|
1054
1185
|
# If a [customer-supplied encryption
|
1055
1186
|
# key](https://cloud.google.com/storage/docs/encryption#customer-supplied)
|
@@ -1082,6 +1213,27 @@ module Google
|
|
1082
1213
|
# access, and allUsers get READER access.
|
1083
1214
|
# @param [Integer] generation Select a specific revision of the file to
|
1084
1215
|
# rewrite. The default is the latest version.
|
1216
|
+
# @param [Integer] if_generation_match Makes the operation conditional
|
1217
|
+
# on whether the destination file's current generation matches the given value.
|
1218
|
+
# Setting to 0 makes the operation succeed only if there are no live
|
1219
|
+
# versions of the file.
|
1220
|
+
# @param [Integer] if_generation_not_match Makes the operation conditional
|
1221
|
+
# on whether the destination file's current generation does not match the given
|
1222
|
+
# value. If no live file exists, the precondition fails. Setting to 0
|
1223
|
+
# makes the operation succeed only if there is a live version of the file.
|
1224
|
+
# @param [Integer] if_metageneration_match Makes the operation conditional
|
1225
|
+
# on whether the destination file's current metageneration matches the given value.
|
1226
|
+
# @param [Integer] if_metageneration_not_match Makes the operation
|
1227
|
+
# conditional on whether the destination file's current metageneration does not
|
1228
|
+
# match the given value.
|
1229
|
+
# @param [Integer] if_source_generation_match Makes the operation conditional on
|
1230
|
+
# whether the source object's current generation matches the given value.
|
1231
|
+
# @param [Integer] if_source_generation_not_match Makes the operation conditional
|
1232
|
+
# on whether the source object's current generation does not match the given value.
|
1233
|
+
# @param [Integer] if_source_metageneration_match Makes the operation conditional
|
1234
|
+
# on whether the source object's current metageneration matches the given value.
|
1235
|
+
# @param [Integer] if_source_metageneration_not_match Makes the operation conditional
|
1236
|
+
# on whether the source object's current metageneration does not match the given value.
|
1085
1237
|
# @param [String] encryption_key Optional. The customer-supplied,
|
1086
1238
|
# AES-256 encryption key used to decrypt the file, if the existing
|
1087
1239
|
# file is encrypted.
|
@@ -1097,6 +1249,19 @@ module Google
|
|
1097
1249
|
# the same location as the bucket.The Service Account associated with
|
1098
1250
|
# your project requires access to this encryption key. Do not provide
|
1099
1251
|
# if `new_encryption_key` is used.
|
1252
|
+
# @param [Boolean] force_copy_metadata Optional. If `true` and if updates
|
1253
|
+
# are made in a block, the following fields will be copied from the
|
1254
|
+
# source file to the destination file (except when changed by updates):
|
1255
|
+
#
|
1256
|
+
# * `cache_control`
|
1257
|
+
# * `content_disposition`
|
1258
|
+
# * `content_encoding`
|
1259
|
+
# * `content_language`
|
1260
|
+
# * `content_type`
|
1261
|
+
# * `metadata`
|
1262
|
+
#
|
1263
|
+
# If `nil` or `false`, only the updates made in the yielded block will
|
1264
|
+
# be applied to the destination object. The default is `nil`.
|
1100
1265
|
# @yield [file] a block yielding a delegate object for updating
|
1101
1266
|
#
|
1102
1267
|
# @return [Google::Cloud::Storage::File]
|
@@ -1161,7 +1326,7 @@ module Google
|
|
1161
1326
|
# cipher.encrypt
|
1162
1327
|
# new_key = cipher.random_key
|
1163
1328
|
#
|
1164
|
-
# file = bucket.file "path/to/my-file.ext"
|
1329
|
+
# file = bucket.file "path/to/my-file.ext", encryption_key: old_key
|
1165
1330
|
# file.rewrite "new-destination-bucket",
|
1166
1331
|
# "path/to/destination/file.ext",
|
1167
1332
|
# encryption_key: old_key,
|
@@ -1182,7 +1347,7 @@ module Google
|
|
1182
1347
|
# # Old customer-supplied key was stored securely for later use.
|
1183
1348
|
# old_key = "y\x03\"\x0E\xB6\xD3\x9B\x0E\xAB*\x19\xFAv\xDEY\xBEI..."
|
1184
1349
|
#
|
1185
|
-
# file = bucket.file "path/to/my-file.ext"
|
1350
|
+
# file = bucket.file "path/to/my-file.ext", encryption_key: old_key
|
1186
1351
|
# file.rewrite "new-destination-bucket",
|
1187
1352
|
# "path/to/destination/file.ext",
|
1188
1353
|
# encryption_key: old_key,
|
@@ -1190,27 +1355,51 @@ module Google
|
|
1190
1355
|
# f.metadata["rewritten_from"] = "#{file.bucket}/#{file.name}"
|
1191
1356
|
# end
|
1192
1357
|
#
|
1193
|
-
def rewrite dest_bucket_or_path,
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1358
|
+
def rewrite dest_bucket_or_path,
|
1359
|
+
dest_path = nil,
|
1360
|
+
acl: nil,
|
1361
|
+
generation: nil,
|
1362
|
+
if_generation_match: nil,
|
1363
|
+
if_generation_not_match: nil,
|
1364
|
+
if_metageneration_match: nil,
|
1365
|
+
if_metageneration_not_match: nil,
|
1366
|
+
if_source_generation_match: nil,
|
1367
|
+
if_source_generation_not_match: nil,
|
1368
|
+
if_source_metageneration_match: nil,
|
1369
|
+
if_source_metageneration_not_match: nil,
|
1370
|
+
encryption_key: nil,
|
1371
|
+
new_encryption_key: nil,
|
1372
|
+
new_kms_key: nil,
|
1373
|
+
force_copy_metadata: nil
|
1197
1374
|
ensure_service!
|
1198
|
-
dest_bucket, dest_path = fix_rewrite_args dest_bucket_or_path,
|
1199
|
-
dest_path
|
1375
|
+
dest_bucket, dest_path = fix_rewrite_args dest_bucket_or_path, dest_path
|
1200
1376
|
|
1201
1377
|
update_gapi = nil
|
1202
1378
|
if block_given?
|
1203
|
-
updater = Updater.new gapi
|
1379
|
+
updater = Updater.new gapi.dup
|
1204
1380
|
yield updater
|
1205
1381
|
updater.check_for_changed_metadata!
|
1206
1382
|
if updater.updates.any?
|
1207
|
-
|
1383
|
+
attributes = force_copy_metadata ? (Updater::COPY_ATTRS + updater.updates).uniq : updater.updates
|
1384
|
+
update_gapi = self.class.gapi_from_attrs updater.gapi, attributes
|
1208
1385
|
end
|
1209
1386
|
end
|
1210
1387
|
|
1211
|
-
new_gapi = rewrite_gapi bucket,
|
1212
|
-
|
1213
|
-
|
1388
|
+
new_gapi = rewrite_gapi bucket,
|
1389
|
+
name,
|
1390
|
+
update_gapi,
|
1391
|
+
new_bucket: dest_bucket,
|
1392
|
+
new_name: dest_path,
|
1393
|
+
acl: acl,
|
1394
|
+
generation: generation,
|
1395
|
+
if_generation_match: if_generation_match,
|
1396
|
+
if_generation_not_match: if_generation_not_match,
|
1397
|
+
if_metageneration_match: if_metageneration_match,
|
1398
|
+
if_metageneration_not_match: if_metageneration_not_match,
|
1399
|
+
if_source_generation_match: if_source_generation_match,
|
1400
|
+
if_source_generation_not_match: if_source_generation_not_match,
|
1401
|
+
if_source_metageneration_match: if_source_metageneration_match,
|
1402
|
+
if_source_metageneration_not_match: if_source_metageneration_not_match,
|
1214
1403
|
encryption_key: encryption_key,
|
1215
1404
|
new_encryption_key: new_encryption_key,
|
1216
1405
|
new_kms_key: new_kms_key,
|
@@ -1307,6 +1496,20 @@ module Google
|
|
1307
1496
|
# {#generation}. The default behavior is to delete the latest version
|
1308
1497
|
# of the file (regardless of the version to which the file is set,
|
1309
1498
|
# which is the version returned by {#generation}.)
|
1499
|
+
# @param [Integer] if_generation_match Makes the operation conditional
|
1500
|
+
# on whether the file's current generation matches the given value.
|
1501
|
+
# Setting to 0 makes the operation succeed only if there are no live
|
1502
|
+
# versions of the file.
|
1503
|
+
# @param [Integer] if_generation_not_match Makes the operation conditional
|
1504
|
+
# on whether the file's current generation does not match the given
|
1505
|
+
# value. If no live file exists, the precondition fails. Setting to 0
|
1506
|
+
# makes the operation succeed only if there is a live version of the file.
|
1507
|
+
# @param [Integer] if_metageneration_match Makes the operation conditional
|
1508
|
+
# on whether the file's current metageneration matches the given value.
|
1509
|
+
# @param [Integer] if_metageneration_not_match Makes the operation
|
1510
|
+
# conditional on whether the file's current metageneration does not
|
1511
|
+
# match the given value.
|
1512
|
+
#
|
1310
1513
|
# @return [Boolean] Returns `true` if the file was deleted.
|
1311
1514
|
#
|
1312
1515
|
# @example
|
@@ -1339,11 +1542,21 @@ module Google
|
|
1339
1542
|
# file = bucket.file "path/to/my-file.ext"
|
1340
1543
|
# file.delete generation: 123456
|
1341
1544
|
#
|
1342
|
-
def delete generation: nil
|
1545
|
+
def delete generation: nil,
|
1546
|
+
if_generation_match: nil,
|
1547
|
+
if_generation_not_match: nil,
|
1548
|
+
if_metageneration_match: nil,
|
1549
|
+
if_metageneration_not_match: nil
|
1343
1550
|
generation = self.generation if generation == true
|
1344
1551
|
ensure_service!
|
1345
|
-
service.delete_file bucket,
|
1346
|
-
|
1552
|
+
service.delete_file bucket,
|
1553
|
+
name,
|
1554
|
+
generation: generation,
|
1555
|
+
if_generation_match: if_generation_match,
|
1556
|
+
if_generation_not_match: if_generation_not_match,
|
1557
|
+
if_metageneration_match: if_metageneration_match,
|
1558
|
+
if_metageneration_not_match: if_metageneration_not_match,
|
1559
|
+
user_project: user_project
|
1347
1560
|
true
|
1348
1561
|
end
|
1349
1562
|
|
@@ -1400,7 +1613,7 @@ module Google
|
|
1400
1613
|
# A {SignedUrlUnavailable} is raised if the service account credentials
|
1401
1614
|
# are missing. Service account credentials are acquired by following the
|
1402
1615
|
# steps in [Service Account Authentication](
|
1403
|
-
# https://cloud.google.com/
|
1616
|
+
# https://cloud.google.com/iam/docs/service-accounts).
|
1404
1617
|
#
|
1405
1618
|
# @see https://cloud.google.com/storage/docs/access-control/signed-urls
|
1406
1619
|
# Signed URLs guide
|
@@ -1425,10 +1638,22 @@ module Google
|
|
1425
1638
|
# use the signed URL.
|
1426
1639
|
# @param [String] issuer Service Account's Client Email.
|
1427
1640
|
# @param [String] client_email Service Account's Client Email.
|
1428
|
-
# @param [OpenSSL::PKey::RSA, String] signing_key Service Account's
|
1429
|
-
# Private Key
|
1430
|
-
#
|
1431
|
-
#
|
1641
|
+
# @param [OpenSSL::PKey::RSA, String, Proc] signing_key Service Account's
|
1642
|
+
# Private Key or a Proc that accepts a single String parameter and returns a
|
1643
|
+
# RSA SHA256 signature using a valid Google Service Account Private Key.
|
1644
|
+
# @param [OpenSSL::PKey::RSA, String, Proc] private_key Service Account's
|
1645
|
+
# Private Key or a Proc that accepts a single String parameter and returns a
|
1646
|
+
# RSA SHA256 signature using a valid Google Service Account Private Key.
|
1647
|
+
# @param [OpenSSL::PKey::RSA, String, Proc] signer Service Account's
|
1648
|
+
# Private Key or a Proc that accepts a single String parameter and returns a
|
1649
|
+
# RSA SHA256 signature using a valid Google Service Account Private Key.
|
1650
|
+
#
|
1651
|
+
# When using this method in environments such as GAE Flexible Environment,
|
1652
|
+
# GKE, or Cloud Functions where the private key is unavailable, it may be
|
1653
|
+
# necessary to provide a Proc (or lambda) via the signer parameter. This
|
1654
|
+
# Proc should return a signature created using a RPC call to the
|
1655
|
+
# [Service Account Credentials signBlob](https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob)
|
1656
|
+
# method as shown in the example below.
|
1432
1657
|
# @param [Hash] query Query string parameters to include in the signed
|
1433
1658
|
# URL. The given parameters are not verified by the signature.
|
1434
1659
|
#
|
@@ -1437,11 +1662,29 @@ module Google
|
|
1437
1662
|
# using the URL, but only when the file resource is missing the
|
1438
1663
|
# corresponding values. (These values can be permanently set using
|
1439
1664
|
# {#content_disposition=} and {#content_type=}.)
|
1665
|
+
# @param [String] scheme The URL scheme. The default value is `HTTPS`.
|
1666
|
+
# @param [Boolean] virtual_hosted_style Whether to use a virtual hosted-style
|
1667
|
+
# hostname, which adds the bucket into the host portion of the URI rather
|
1668
|
+
# than the path, e.g. `https://mybucket.storage.googleapis.com/...`.
|
1669
|
+
# For V4 signing, this also sets the `host` header in the canonicalized
|
1670
|
+
# extension headers to the virtual hosted-style host, unless that header is
|
1671
|
+
# supplied via the `headers` param. The default value of `false` uses the
|
1672
|
+
# form of `https://storage.googleapis.com/mybucket`.
|
1673
|
+
# @param [String] bucket_bound_hostname Use a bucket-bound hostname, which
|
1674
|
+
# replaces the `storage.googleapis.com` host with the name of a `CNAME`
|
1675
|
+
# bucket, e.g. a bucket named `gcs-subdomain.my.domain.tld`, or a Google
|
1676
|
+
# Cloud Load Balancer which routes to a bucket you own, e.g.
|
1677
|
+
# `my-load-balancer-domain.tld`.
|
1440
1678
|
# @param [Symbol, String] version The version of the signed credential
|
1441
1679
|
# to create. Must be one of `:v2` or `:v4`. The default value is
|
1442
1680
|
# `:v2`.
|
1443
1681
|
#
|
1444
|
-
# @return [String]
|
1682
|
+
# @return [String] The signed URL.
|
1683
|
+
#
|
1684
|
+
# @raise [SignedUrlUnavailable] If the service account credentials
|
1685
|
+
# are missing. Service account credentials are acquired by following the
|
1686
|
+
# steps in [Service Account Authentication](
|
1687
|
+
# https://cloud.google.com/iam/docs/service-accounts).
|
1445
1688
|
#
|
1446
1689
|
# @example
|
1447
1690
|
# require "google/cloud/storage"
|
@@ -1461,7 +1704,7 @@ module Google
|
|
1461
1704
|
# file = bucket.file "avatars/heidi/400x400.png"
|
1462
1705
|
# shared_url = file.signed_url expires: 300, # 5 minutes from now
|
1463
1706
|
# version: :v4
|
1464
|
-
|
1707
|
+
#
|
1465
1708
|
# @example Using the `issuer` and `signing_key` options:
|
1466
1709
|
# require "google/cloud/storage"
|
1467
1710
|
#
|
@@ -1501,28 +1744,85 @@ module Google
|
|
1501
1744
|
# # Send the `x-goog-resumable:start` header and the content type
|
1502
1745
|
# # with the resumable upload POST request.
|
1503
1746
|
#
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1747
|
+
# @example Using Cloud IAMCredentials signBlob to create the signature:
|
1748
|
+
# require "google/cloud/storage"
|
1749
|
+
# require "google/apis/iamcredentials_v1"
|
1750
|
+
# require "googleauth"
|
1751
|
+
#
|
1752
|
+
# # Issuer is the service account email that the Signed URL will be signed with
|
1753
|
+
# # and any permission granted in the Signed URL must be granted to the
|
1754
|
+
# # Google Service Account.
|
1755
|
+
# issuer = "service-account@project-id.iam.gserviceaccount.com"
|
1756
|
+
#
|
1757
|
+
# # Create a lambda that accepts the string_to_sign
|
1758
|
+
# signer = lambda do |string_to_sign|
|
1759
|
+
# IAMCredentials = Google::Apis::IamcredentialsV1
|
1760
|
+
# iam_client = IAMCredentials::IAMCredentialsService.new
|
1761
|
+
#
|
1762
|
+
# # Get the environment configured authorization
|
1763
|
+
# scopes = ["https://www.googleapis.com/auth/iam"]
|
1764
|
+
# iam_client.authorization = Google::Auth.get_application_default scopes
|
1765
|
+
#
|
1766
|
+
# request = Google::Apis::IamcredentialsV1::SignBlobRequest.new(
|
1767
|
+
# payload: string_to_sign
|
1768
|
+
# )
|
1769
|
+
# resource = "projects/-/serviceAccounts/#{issuer}"
|
1770
|
+
# response = iam_client.sign_service_account_blob resource, request
|
1771
|
+
# response.signed_blob
|
1772
|
+
# end
|
1773
|
+
#
|
1774
|
+
# storage = Google::Cloud::Storage.new
|
1775
|
+
#
|
1776
|
+
# bucket = storage.bucket "my-todo-app"
|
1777
|
+
# file = bucket.file "avatars/heidi/400x400.png", skip_lookup: true
|
1778
|
+
# url = file.signed_url method: "GET", issuer: issuer,
|
1779
|
+
# signer: signer
|
1780
|
+
#
|
1781
|
+
def signed_url method: "GET",
|
1782
|
+
expires: nil,
|
1783
|
+
content_type: nil,
|
1784
|
+
content_md5: nil,
|
1785
|
+
headers: nil,
|
1786
|
+
issuer: nil,
|
1787
|
+
client_email: nil,
|
1788
|
+
signing_key: nil,
|
1789
|
+
private_key: nil,
|
1790
|
+
signer: nil,
|
1791
|
+
query: nil,
|
1792
|
+
scheme: "HTTPS",
|
1793
|
+
virtual_hosted_style: nil,
|
1794
|
+
bucket_bound_hostname: nil,
|
1795
|
+
version: nil
|
1508
1796
|
ensure_service!
|
1509
1797
|
version ||= :v2
|
1510
1798
|
case version.to_sym
|
1511
1799
|
when :v2
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1800
|
+
sign = File::SignerV2.from_file self
|
1801
|
+
sign.signed_url method: method,
|
1802
|
+
expires: expires,
|
1803
|
+
headers: headers,
|
1804
|
+
content_type: content_type,
|
1805
|
+
content_md5: content_md5,
|
1806
|
+
issuer: issuer,
|
1807
|
+
client_email: client_email,
|
1808
|
+
signing_key: signing_key,
|
1809
|
+
private_key: private_key,
|
1810
|
+
signer: signer,
|
1811
|
+
query: query
|
1519
1812
|
when :v4
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1813
|
+
sign = File::SignerV4.from_file self
|
1814
|
+
sign.signed_url method: method,
|
1815
|
+
expires: expires,
|
1816
|
+
headers: headers,
|
1817
|
+
issuer: issuer,
|
1818
|
+
client_email: client_email,
|
1819
|
+
signing_key: signing_key,
|
1820
|
+
private_key: private_key,
|
1821
|
+
signer: signer,
|
1822
|
+
query: query,
|
1823
|
+
scheme: scheme,
|
1824
|
+
virtual_hosted_style: virtual_hosted_style,
|
1825
|
+
bucket_bound_hostname: bucket_bound_hostname
|
1526
1826
|
else
|
1527
1827
|
raise ArgumentError, "version '#{version}' not supported"
|
1528
1828
|
end
|
@@ -1679,6 +1979,21 @@ module Google
|
|
1679
1979
|
end
|
1680
1980
|
end
|
1681
1981
|
|
1982
|
+
##
|
1983
|
+
# @private
|
1984
|
+
#
|
1985
|
+
def self.gapi_from_attrs gapi, attributes
|
1986
|
+
attributes.flatten!
|
1987
|
+
return nil if attributes.empty?
|
1988
|
+
attr_params = Hash[attributes.map do |attr|
|
1989
|
+
[attr, gapi.send(attr)]
|
1990
|
+
end]
|
1991
|
+
# Sending nil metadata results in an Apiary runtime error:
|
1992
|
+
# NoMethodError: undefined method `each' for nil:NilClass
|
1993
|
+
attr_params.reject! { |k, v| k == :metadata && v.nil? }
|
1994
|
+
Google::Apis::StorageV1::Object.new(**attr_params)
|
1995
|
+
end
|
1996
|
+
|
1682
1997
|
protected
|
1683
1998
|
|
1684
1999
|
##
|
@@ -1695,53 +2010,89 @@ module Google
|
|
1695
2010
|
reload! generation: true
|
1696
2011
|
end
|
1697
2012
|
|
1698
|
-
def update_gapi!
|
2013
|
+
def update_gapi! attributes,
|
2014
|
+
generation: nil,
|
2015
|
+
if_generation_match: nil,
|
2016
|
+
if_generation_not_match: nil,
|
2017
|
+
if_metageneration_match: nil,
|
2018
|
+
if_metageneration_not_match: nil
|
2019
|
+
attributes = Array(attributes)
|
1699
2020
|
attributes.flatten!
|
1700
2021
|
return if attributes.empty?
|
1701
|
-
update_gapi = gapi_from_attrs attributes
|
2022
|
+
update_gapi = self.class.gapi_from_attrs @gapi, attributes
|
1702
2023
|
return if update_gapi.nil?
|
1703
2024
|
|
1704
2025
|
ensure_service!
|
1705
2026
|
|
1706
|
-
rewrite_attrs =
|
2027
|
+
rewrite_attrs = [:storage_class, :kms_key_name]
|
1707
2028
|
@gapi = if attributes.any? { |a| rewrite_attrs.include? a }
|
1708
|
-
rewrite_gapi
|
1709
|
-
|
2029
|
+
rewrite_gapi bucket,
|
2030
|
+
name,
|
2031
|
+
update_gapi,
|
2032
|
+
generation: generation,
|
2033
|
+
if_generation_match: if_generation_match,
|
2034
|
+
if_generation_not_match: if_generation_not_match,
|
2035
|
+
if_metageneration_match: if_metageneration_match,
|
2036
|
+
if_metageneration_not_match: if_metageneration_not_match,
|
2037
|
+
user_project: user_project
|
1710
2038
|
else
|
1711
|
-
service.patch_file
|
1712
|
-
|
2039
|
+
service.patch_file bucket,
|
2040
|
+
name,
|
2041
|
+
update_gapi,
|
2042
|
+
generation: generation,
|
2043
|
+
if_generation_match: if_generation_match,
|
2044
|
+
if_generation_not_match: if_generation_not_match,
|
2045
|
+
if_metageneration_match: if_metageneration_match,
|
2046
|
+
if_metageneration_not_match: if_metageneration_not_match,
|
2047
|
+
user_project: user_project
|
1713
2048
|
end
|
1714
2049
|
end
|
1715
2050
|
|
1716
|
-
def
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
2051
|
+
def rewrite_gapi bucket,
|
2052
|
+
name,
|
2053
|
+
updated_gapi,
|
2054
|
+
new_bucket: nil,
|
2055
|
+
new_name: nil,
|
2056
|
+
acl: nil,
|
2057
|
+
generation: nil,
|
2058
|
+
if_generation_match: nil,
|
2059
|
+
if_generation_not_match: nil,
|
2060
|
+
if_metageneration_match: nil,
|
2061
|
+
if_metageneration_not_match: nil,
|
2062
|
+
if_source_generation_match: nil,
|
2063
|
+
if_source_generation_not_match: nil,
|
2064
|
+
if_source_metageneration_match: nil,
|
2065
|
+
if_source_metageneration_not_match: nil,
|
2066
|
+
encryption_key: nil,
|
2067
|
+
new_encryption_key: nil,
|
2068
|
+
new_kms_key: nil,
|
1729
2069
|
user_project: nil
|
1730
2070
|
new_bucket ||= bucket
|
1731
2071
|
new_name ||= name
|
1732
|
-
options = {
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
2072
|
+
options = {
|
2073
|
+
acl: File::Acl.predefined_rule_for(acl),
|
2074
|
+
generation: generation,
|
2075
|
+
if_generation_match: if_generation_match,
|
2076
|
+
if_generation_not_match: if_generation_not_match,
|
2077
|
+
if_metageneration_match: if_metageneration_match,
|
2078
|
+
if_metageneration_not_match: if_metageneration_not_match,
|
2079
|
+
if_source_generation_match: if_source_generation_match,
|
2080
|
+
if_source_generation_not_match: if_source_generation_not_match,
|
2081
|
+
if_source_metageneration_match: if_source_metageneration_match,
|
2082
|
+
if_source_metageneration_not_match: if_source_metageneration_not_match,
|
2083
|
+
source_key: encryption_key,
|
2084
|
+
destination_key: new_encryption_key,
|
2085
|
+
destination_kms_key: new_kms_key,
|
2086
|
+
user_project: user_project
|
2087
|
+
}.delete_if { |_k, v| v.nil? }
|
1737
2088
|
|
1738
2089
|
resp = service.rewrite_file \
|
1739
|
-
bucket, name, new_bucket, new_name, updated_gapi, options
|
2090
|
+
bucket, name, new_bucket, new_name, updated_gapi, **options
|
1740
2091
|
until resp.done
|
1741
2092
|
sleep 1
|
1742
2093
|
retry_options = options.merge token: resp.rewrite_token
|
1743
2094
|
resp = service.rewrite_file \
|
1744
|
-
bucket, name, new_bucket, new_name, updated_gapi, retry_options
|
2095
|
+
bucket, name, new_bucket, new_name, updated_gapi, **retry_options
|
1745
2096
|
end
|
1746
2097
|
resp.resource
|
1747
2098
|
end
|
@@ -1792,11 +2143,26 @@ module Google
|
|
1792
2143
|
# Yielded to a block to accumulate changes for a patch request.
|
1793
2144
|
class Updater < File
|
1794
2145
|
# @private
|
1795
|
-
attr_reader :updates
|
2146
|
+
attr_reader :updates, :gapi
|
2147
|
+
|
2148
|
+
##
|
2149
|
+
# @private
|
2150
|
+
# Whitelist of Google::Apis::StorageV1::Object attributes to be
|
2151
|
+
# copied when File#copy or File#rewrite is called with
|
2152
|
+
# `force_copy_metadata: true`.
|
2153
|
+
COPY_ATTRS = [
|
2154
|
+
:cache_control,
|
2155
|
+
:content_disposition,
|
2156
|
+
:content_encoding,
|
2157
|
+
:content_language,
|
2158
|
+
:content_type,
|
2159
|
+
:metadata
|
2160
|
+
].freeze
|
1796
2161
|
|
1797
2162
|
##
|
1798
2163
|
# @private Create an Updater object.
|
1799
2164
|
def initialize gapi
|
2165
|
+
super()
|
1800
2166
|
@updates = []
|
1801
2167
|
@gapi = gapi
|
1802
2168
|
@metadata ||= @gapi.metadata.to_h.dup
|