google-cloud-storage 1.18.1 → 1.44.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|