Skip to content

Commit f5aacbd

Browse files
Updated the Plugin Version and cherrypicked Ignore 409 Conflicts
1 parent 55d71a6 commit f5aacbd

3 files changed

Lines changed: 72 additions & 21 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
<groupId>io.cdap.plugin</groupId>
2222
<artifactId>google-cloud</artifactId>
23-
<version>0.24.6</version>
23+
<version>0.24.7-SNAPSHOT</version>
2424
<name>Google Cloud Plugins</name>
2525
<packaging>jar</packaging>
2626
<description>Plugins for Google Big Query</description>

src/main/java/io/cdap/plugin/gcp/bigquery/source/BigQuerySourceUtils.java

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,30 +87,43 @@ public static String getOrCreateBucket(Configuration configuration,
8787
// By default, this option is false, meaning the job can not delete the bucket. So enable it only when bucket name
8888
// is not provided.
8989
configuration.setBoolean("fs.gs.bucket.delete.enable", true);
90-
GCPUtils.createBucket(storage, bucket, dataset.getLocation(), cmekKeyName);
90+
createBucket(storage, bucket, dataset, cmekKeyName);
9191
} else if (storage != null && storage.get(bucket) == null) {
92-
try {
93-
GCPUtils.createBucket(storage, bucket, dataset.getLocation(), cmekKeyName);
94-
} catch (StorageException e) {
95-
if (e.getCode() == 409) {
96-
// A conflict means the bucket already exists
97-
// This most likely means multiple stages in the same pipeline are trying to create the same bucket.
98-
// Ignore this and move on, since all that matters is that the bucket exists.
99-
return bucket;
100-
}
101-
String errorMessage = String.format("Unable to create Cloud Storage bucket '%s' in the same "
102-
+ "location ('%s') as BigQuery dataset '%s'. " + "Please use a bucket "
103-
+ "that is in the same location as the dataset. For more details, see %s",
104-
bucket, dataset.getLocation(), dataset.getDatasetId().getDataset(),
105-
GCPUtils.GCS_SUPPORTED_DOC_URL);
106-
throw ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN),
107-
errorMessage, e.getMessage(), ErrorType.USER, true, ErrorCodeType.HTTP,
108-
String.valueOf(e.getCode()), GCPUtils.GCS_SUPPORTED_DOC_URL, e);
109-
}
92+
createBucket(storage, bucket, dataset, cmekKeyName);
11093
}
11194
return bucket;
11295
}
11396

97+
private static void createBucket(@Nullable Storage storage,
98+
String bucket,
99+
Dataset dataset,
100+
@Nullable CryptoKeyName cmekKeyName) {
101+
if (storage == null) {
102+
return;
103+
}
104+
try {
105+
GCPUtils.createBucket(storage, bucket, dataset.getLocation(), cmekKeyName);
106+
} catch (StorageException e) {
107+
if (e.getCode() == 409) {
108+
// A conflict means the bucket already exists.
109+
// This most likely means multiple stages in the same pipeline are trying to create the same bucket,
110+
// or a retry occurred after a successful bucket creation.
111+
// Ignore this and move on, since all that matters is that the bucket exists.
112+
LOG.debug("Bucket '{}' already exists, ignoring 409 Conflict: {}", bucket, e.getMessage());
113+
return;
114+
}
115+
String datasetName = dataset.getDatasetId() != null ? dataset.getDatasetId().getDataset() : "";
116+
String errorMessage = String.format("Unable to create Cloud Storage bucket '%s' in the same "
117+
+ "location ('%s') as BigQuery dataset '%s'. " + "Please use a bucket "
118+
+ "that is in the same location as the dataset. For more details, see %s",
119+
bucket, dataset.getLocation(), datasetName,
120+
GCPUtils.GCS_SUPPORTED_DOC_URL);
121+
throw ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN),
122+
errorMessage, e.getMessage(), ErrorType.USER, true, ErrorCodeType.HTTP,
123+
String.valueOf(e.getCode()), GCPUtils.GCS_SUPPORTED_DOC_URL, e);
124+
}
125+
}
126+
114127
/**
115128
* Sets up service account credentials into supplied Hadoop configuration.
116129
*

src/test/java/io/cdap/plugin/gcp/bigquery/source/BigQuerySourceUtilsTest.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
package io.cdap.plugin.gcp.bigquery.source;
1818

1919
import com.google.cloud.bigquery.Dataset;
20+
import com.google.cloud.bigquery.DatasetId;
2021
import com.google.cloud.storage.Bucket;
22+
import com.google.cloud.storage.BucketInfo;
2123
import com.google.cloud.storage.Storage;
24+
import com.google.cloud.storage.StorageException;
25+
import io.cdap.cdap.api.exception.ProgramFailureException;
2226
import org.apache.hadoop.conf.Configuration;
2327
import org.junit.Assert;
2428
import org.junit.Test;
2529
import org.mockito.Mockito;
2630

2731
import java.io.IOException;
28-
import java.lang.reflect.Field;
2932

3033
public class BigQuerySourceUtilsTest {
3134

@@ -50,4 +53,39 @@ public void getOrCreateBucket() throws IllegalAccessException, NoSuchFieldExcept
5053
"some-path", null);
5154
Assert.assertEquals("a-bucket", bucket2);
5255
}
56+
57+
@Test
58+
public void getOrCreateBucket_nullBucketConflict409_returnsBucketAndEnablesDelete() throws IOException {
59+
Configuration configuration = new Configuration();
60+
BigQuerySourceConfig config = BigQuerySourceConfig.builder().build();
61+
Dataset ds = Mockito.mock(Dataset.class);
62+
Storage st = Mockito.mock(Storage.class);
63+
Mockito.when(st.create(Mockito.any(BucketInfo.class)))
64+
.thenThrow(new StorageException(409, "Your previous request to create the named bucket succeeded "
65+
+ "and you already own it."));
66+
67+
String bucket = BigQuerySourceUtils.getOrCreateBucket(configuration, st, config.getBucket(), ds,
68+
"some-path", null);
69+
70+
Assert.assertEquals("bq-source-bucket-some-path", bucket);
71+
Assert.assertTrue(configuration.getBoolean("fs.gs.bucket.delete.enable", false));
72+
}
73+
74+
@Test
75+
public void getOrCreateBucket_nullBucketNon409Error_throwsProgramFailureException() {
76+
Configuration configuration = new Configuration();
77+
BigQuerySourceConfig config = BigQuerySourceConfig.builder().build();
78+
Dataset ds = Mockito.mock(Dataset.class);
79+
DatasetId datasetId = DatasetId.of("project", "dataset");
80+
Mockito.when(ds.getDatasetId()).thenReturn(datasetId);
81+
Mockito.when(ds.getLocation()).thenReturn("US");
82+
Storage st = Mockito.mock(Storage.class);
83+
Mockito.when(st.create(Mockito.any(BucketInfo.class)))
84+
.thenThrow(new StorageException(403, "Access denied"));
85+
86+
ProgramFailureException exception = Assert.assertThrows(ProgramFailureException.class, () ->
87+
BigQuerySourceUtils.getOrCreateBucket(configuration, st, config.getBucket(), ds, "some-path", null));
88+
89+
Assert.assertTrue(exception.getMessage().contains("Access denied"));
90+
}
5391
}

0 commit comments

Comments
 (0)