-
Notifications
You must be signed in to change notification settings - Fork 244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for userProject in java-storage #1205
base: branch-2.2.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ | |
import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.PartCleanupStrategy; | ||
import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.PartNamingStrategy; | ||
import com.google.cloud.storage.Storage; | ||
import com.google.cloud.storage.StorageException; | ||
import com.google.cloud.storage.StorageOptions; | ||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.base.MoreObjects; | ||
|
@@ -74,6 +75,7 @@ | |
|
||
private final GoogleCloudStorageOptions storageOptions; | ||
private final Storage storage; | ||
private final RequesterPaysManager requesterPaysManager; | ||
|
||
// Error extractor to map APi exception to meaningful ErrorTypes. | ||
private static final ErrorTypeExtractor errorExtractor = GrpcErrorTypeExtractor.INSTANCE; | ||
|
@@ -114,6 +116,8 @@ | |
? createStorage( | ||
credentials, options, gRPCInterceptors, pCUExecutorService, downscopedAccessTokenFn) | ||
: clientLibraryStorage; | ||
this.requesterPaysManager = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like we are adding more than the userProject in this PR? If yes, please update the PR descriptions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. it is very specific to requesterPays feature. RequesterPaysManager is created with an idea that multiple GoogleCloundStorage client will be able to share the same caching logic of requesterPaysInfo. |
||
new RequesterPaysManager(options.getRequesterPaysOptions(), this::shouldRequesterPay); | ||
} | ||
|
||
@Override | ||
|
@@ -137,7 +141,11 @@ | |
} | ||
|
||
return new GoogleCloudStorageClientWriteChannel( | ||
storage, storageOptions, resourceIdWithGeneration, options); | ||
storage, | ||
storageOptions, | ||
resourceIdWithGeneration, | ||
options, | ||
requesterPaysManager::requesterShouldPay); | ||
} | ||
|
||
@Override | ||
|
@@ -157,7 +165,8 @@ | |
itemInfo == null ? getItemInfo(resourceId) : itemInfo, | ||
readOptions, | ||
errorExtractor, | ||
storageOptions); | ||
storageOptions, | ||
requesterPaysManager::requesterShouldPay); | ||
} | ||
|
||
@Override | ||
|
@@ -270,6 +279,15 @@ | |
.getService(); | ||
} | ||
|
||
private Boolean shouldRequesterPay(String bucketName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not move this to RequestPaysManager? |
||
try { | ||
storage.testIamPermissions(bucketName, ImmutableList.of("storage.buckets.get")); | ||
Check warning on line 284 in gcsio/src/main/java/com/google/cloud/hadoop/gcsio/GoogleCloudStorageClientImpl.java
|
||
} catch (StorageException e) { | ||
return errorExtractor.userProjectMissing(e); | ||
} | ||
return false; | ||
Check warning on line 288 in gcsio/src/main/java/com/google/cloud/hadoop/gcsio/GoogleCloudStorageClientImpl.java
|
||
} | ||
|
||
private static ImmutableMap<String, String> getUpdatedHeadersWithUserAgent( | ||
GoogleCloudStorageOptions storageOptions) { | ||
ImmutableMap<String, String> httpRequestHeaders = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,7 @@ | |
import java.nio.channels.SeekableByteChannel; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
import javax.annotation.Nullable; | ||
|
||
/** Provides seekable read access to GCS via java-storage library. */ | ||
|
@@ -64,6 +65,7 @@ | |
private long objectSize; | ||
private final ErrorTypeExtractor errorExtractor; | ||
private ContentReadChannel contentReadChannel; | ||
private Function<String, Boolean> requesterShouldPay; | ||
private boolean gzipEncoded = false; | ||
private boolean open = true; | ||
|
||
|
@@ -76,7 +78,8 @@ | |
GoogleCloudStorageItemInfo itemInfo, | ||
GoogleCloudStorageReadOptions readOptions, | ||
ErrorTypeExtractor errorExtractor, | ||
GoogleCloudStorageOptions storageOptions) | ||
GoogleCloudStorageOptions storageOptions, | ||
Function<String, Boolean> requesterShouldPay) | ||
throws IOException { | ||
validate(itemInfo); | ||
this.storage = storage; | ||
|
@@ -87,6 +90,7 @@ | |
this.readOptions = readOptions; | ||
this.storageOptions = storageOptions; | ||
this.contentReadChannel = new ContentReadChannel(readOptions, resourceId); | ||
this.requesterShouldPay = requesterShouldPay; | ||
initMetadata(itemInfo.getContentEncoding(), itemInfo.getSize()); | ||
} | ||
|
||
|
@@ -582,6 +586,10 @@ | |
blobReadOptions.add( | ||
BlobSourceOption.decryptionKey(storageOptions.getEncryptionKey().value())); | ||
} | ||
if (requesterShouldPay.apply((blobId.getBucket()))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't we have this as a boolean value per GCSClientReadChannel? i.e. what is the value of recomputing this for reach GCS ReadChannel? |
||
blobReadOptions.add( | ||
BlobSourceOption.userProject(storageOptions.getRequesterPaysOptions().getProjectId())); | ||
Check warning on line 591 in gcsio/src/main/java/com/google/cloud/hadoop/gcsio/GoogleCloudStorageClientReadChannel.java
|
||
} | ||
return blobReadOptions.toArray(new BlobSourceOption[blobReadOptions.size()]); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* Copyright 2024 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.google.cloud.hadoop.gcsio; | ||
|
||
import com.google.cloud.hadoop.util.RequesterPaysOptions; | ||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.cache.CacheBuilder; | ||
import com.google.common.cache.CacheLoader; | ||
import com.google.common.cache.LoadingCache; | ||
import java.time.Duration; | ||
import java.util.function.Function; | ||
|
||
@VisibleForTesting | ||
public class RequesterPaysManager { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not using this in the JSON path as well? Looks like now autobuckets will be there in both GCSImpl and GCSClientImpl? |
||
private final RequesterPaysOptions requesterPaysOptions; | ||
private final Function<String, Boolean> shouldRequesterPay; | ||
private final LoadingCache<String, Boolean> autoBuckets = | ||
CacheBuilder.newBuilder() | ||
.expireAfterWrite(Duration.ofHours(1)) | ||
.build( | ||
new CacheLoader<String, Boolean>() { | ||
@Override | ||
public Boolean load(String bucketName) { | ||
return shouldRequesterPay.apply(bucketName); | ||
} | ||
}); | ||
|
||
public RequesterPaysManager( | ||
RequesterPaysOptions requesterPaysOptions, Function<String, Boolean> shouldRequesterPay) { | ||
this.requesterPaysOptions = requesterPaysOptions; | ||
this.shouldRequesterPay = shouldRequesterPay; | ||
} | ||
|
||
public boolean requesterShouldPay(String bucketName) { | ||
if (bucketName == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when can this happen? |
||
return false; | ||
} | ||
switch (requesterPaysOptions.getMode()) { | ||
case ENABLED: | ||
return true; | ||
case CUSTOM: | ||
return requesterPaysOptions.getBuckets().contains(bucketName); | ||
case AUTO: | ||
return autoBuckets.getUnchecked(bucketName); | ||
default: | ||
return false; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we doing this as an interceptor? Why not veneer provide this as a config while creating the client. Even better, they should be able to figure this out from the GCE VM they are running (if it is running from VM), correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is Trace interceptor, here we trace the request for
userProject
filed. If this field is in headers we do log it. It points towards the requesterPays filed being set for gcsbucket in request.Via this code we are not updating the header to use the provided project but other way round. Java-storage client do provide a way for requester-pays to be set.
No. It's a userProject header is the one which carries information of
requesterpays
feature of gcs bucket. This has nothing to with the account/project the VM is in. This feature can even be used from machines which are not part of GCP.