Skip to content

Commit 793c798

Browse files
zhumin8mpeddada1
andauthored
fix: add runtime hints for storage (#2001)
* fix: add runtime hints for sample and add sample test to ci; refactor tests to use unique resources --------- Co-authored-by: mpeddada1 <[email protected]> Co-authored-by: Mridula <[email protected]>
1 parent 2ba4a75 commit 793c798

File tree

11 files changed

+232
-54
lines changed

11 files changed

+232
-54
lines changed

pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@
353353
<profile>
354354
<id>spring-native</id>
355355
<modules>
356+
<module>spring-cloud-gcp-storage</module>
356357
<module>spring-cloud-gcp-vision</module>
357358
</modules>
358359

@@ -379,6 +380,7 @@
379380
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
380381
<systemPropertyVariables>
381382
<!--integration tests are not invoked unless the relevant system property is set to true here. -->
383+
<it.storage>true</it.storage>
382384
<it.vision>true</it.vision>
383385
</systemPropertyVariables>
384386
</configuration>

spring-cloud-gcp-samples/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
<id>native-sample-config</id>
9292
<modules>
9393
<module>spring-cloud-gcp-logging-sample</module>
94+
<module>spring-cloud-gcp-storage-resource-sample</module>
95+
<module>spring-cloud-gcp-integration-storage-sample</module>
9496
<module>spring-cloud-gcp-trace-sample</module>
9597
<module>spring-cloud-gcp-vision-api-sample</module>
9698
</modules>
@@ -135,8 +137,14 @@
135137
</includes>
136138
<systemPropertyVariables>
137139
<it.logging>true</it.logging>
140+
<it.storage>true</it.storage>
141+
<it.vision>true</it.vision>
138142
<it.trace>true</it.trace>
139143
<it.vision>true</it.vision>
144+
<gcs-resource-test-bucket>gcp-storage-resource-bucket-sample</gcs-resource-test-bucket>
145+
<gcs-read-bucket>gcp-storage-bucket-sample-input</gcs-read-bucket>
146+
<gcs-write-bucket>gcp-storage-bucket-sample-output</gcs-write-bucket>
147+
<gcs-local-directory>/tmp/gcp_integration_tests/integration_storage_sample</gcs-local-directory>
140148
</systemPropertyVariables>
141149
</configuration>
142150
</plugin>

spring-cloud-gcp-samples/spring-cloud-gcp-integration-storage-sample/src/main/java/com/example/GcsSpringIntegrationApplication.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,23 @@
4444
@SpringBootApplication
4545
public class GcsSpringIntegrationApplication {
4646

47+
private static final Log LOGGER = LogFactory.getLog(GcsSpringIntegrationApplication.class);
48+
4749
@Value("${gcs-read-bucket}")
4850
private String gcsReadBucket;
4951

5052
@Value("${gcs-write-bucket}")
5153
private String gcsWriteBucket;
5254

53-
@Value("${gcs-local-directory}")
54-
private String localDirectory;
55-
56-
private static final Log LOGGER = LogFactory.getLog(GcsSpringIntegrationApplication.class);
57-
5855
public static void main(String[] args) {
5956
SpringApplication.run(GcsSpringIntegrationApplication.class, args);
6057
}
6158

59+
@Bean("localDirectoryName")
60+
public String localDirectory(@Value("${gcs-local-directory}") String localDirectory) {
61+
return localDirectory;
62+
}
63+
6264
/**
6365
* A file synchronizer that knows how to connect to the remote file system (GCS) and scan it for
6466
* new files and then download the files.
@@ -83,10 +85,11 @@ public GcsInboundFileSynchronizer gcsInboundFileSynchronizer(Storage gcs) {
8385
*/
8486
@Bean
8587
@InboundChannelAdapter(channel = "new-file-channel", poller = @Poller(fixedDelay = "5000"))
86-
public MessageSource<File> synchronizerAdapter(GcsInboundFileSynchronizer synchronizer) {
88+
public MessageSource<File> synchronizerAdapter(
89+
GcsInboundFileSynchronizer synchronizer, String localDirectoryName) {
8790
GcsInboundFileSynchronizingMessageSource syncAdapter =
8891
new GcsInboundFileSynchronizingMessageSource(synchronizer);
89-
syncAdapter.setLocalDirectory(Paths.get(this.localDirectory).toFile());
92+
syncAdapter.setLocalDirectory(Paths.get(localDirectoryName).toFile());
9093

9194
return syncAdapter;
9295
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example;
18+
19+
import java.util.UUID;
20+
import org.springframework.beans.factory.annotation.Value;
21+
import org.springframework.boot.test.context.TestConfiguration;
22+
import org.springframework.context.annotation.Bean;
23+
24+
@TestConfiguration
25+
public class GcsSpringIntegrationTestConfiguration {
26+
27+
private String uniqueDirectory;
28+
29+
public GcsSpringIntegrationTestConfiguration(
30+
@Value("${gcs-local-directory}") String localDirectory) {
31+
uniqueDirectory = String.format("%s-%s", localDirectory, UUID.randomUUID());
32+
}
33+
34+
@Bean("localDirectoryName")
35+
public String uniqueDirectory() {
36+
return uniqueDirectory;
37+
}
38+
}

spring-cloud-gcp-samples/spring-cloud-gcp-integration-storage-sample/src/test/java/com/example/GcsSpringIntegrationTests.java

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
2020

21-
import com.google.api.gax.paging.Page;
2221
import com.google.cloud.storage.Blob;
2322
import com.google.cloud.storage.BlobId;
2423
import com.google.cloud.storage.BlobInfo;
@@ -30,15 +29,19 @@
3029
import java.nio.file.Paths;
3130
import java.util.ArrayList;
3231
import java.util.List;
32+
import java.util.UUID;
3333
import java.util.concurrent.TimeUnit;
34-
import java.util.stream.Collectors;
34+
import java.util.stream.Stream;
35+
import org.apache.commons.logging.Log;
36+
import org.apache.commons.logging.LogFactory;
3537
import org.awaitility.Awaitility;
36-
import org.junit.jupiter.api.AfterEach;
37-
import org.junit.jupiter.api.BeforeEach;
38+
import org.junit.jupiter.api.AfterAll;
3839
import org.junit.jupiter.api.Test;
40+
import org.junit.jupiter.api.TestInstance;
3941
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
4042
import org.junit.jupiter.api.extension.ExtendWith;
4143
import org.springframework.beans.factory.annotation.Autowired;
44+
import org.springframework.beans.factory.annotation.Qualifier;
4245
import org.springframework.beans.factory.annotation.Value;
4346
import org.springframework.boot.test.context.SpringBootTest;
4447
import org.springframework.context.annotation.PropertySource;
@@ -54,44 +57,43 @@
5457
@EnabledIfSystemProperty(named = "it.storage", matches = "true")
5558
@ExtendWith(SpringExtension.class)
5659
@PropertySource("classpath:application.properties")
57-
@SpringBootTest(classes = {GcsSpringIntegrationApplication.class})
60+
@SpringBootTest(
61+
classes = {GcsSpringIntegrationApplication.class, GcsSpringIntegrationTestConfiguration.class},
62+
properties = {"spring.main.allow-bean-definition-overriding=true"})
63+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
5864
class GcsSpringIntegrationTests {
5965

60-
private static final String TEST_FILE_NAME = "test_file";
66+
private static final String TEST_FILE = String.format("test_file_%s", UUID.randomUUID());
67+
private static final Log LOGGER = LogFactory.getLog(GcsSpringIntegrationTests.class);
6168

6269
@Autowired private Storage storage;
6370

71+
@Autowired
72+
@Qualifier("localDirectoryName")
73+
private String uniqueDirectory;
74+
6475
@Value("${gcs-read-bucket}")
6576
private String cloudInputBucket;
6677

6778
@Value("${gcs-write-bucket}")
6879
private String cloudOutputBucket;
6980

70-
@Value("${gcs-local-directory}")
71-
private String outputFolder;
72-
73-
@BeforeEach
74-
void setupTestEnvironment() {
75-
cleanupCloudStorage();
76-
}
77-
78-
@AfterEach
81+
@AfterAll
7982
void teardownTestEnvironment() throws IOException {
8083
cleanupCloudStorage();
81-
cleanupLocalDirectory();
84+
cleanupLocalDirectory(Paths.get(uniqueDirectory));
8285
}
8386

8487
@Test
8588
void testFilePropagatedToLocalDirectory() {
86-
BlobId blobId = BlobId.of(this.cloudInputBucket, TEST_FILE_NAME);
89+
BlobId blobId = BlobId.of(this.cloudInputBucket, TEST_FILE);
8790
BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
8891
this.storage.create(blobInfo, "Hello World!".getBytes(StandardCharsets.UTF_8));
89-
9092
Awaitility.await()
91-
.atMost(15, TimeUnit.SECONDS)
93+
.atMost(30, TimeUnit.SECONDS)
9294
.untilAsserted(
9395
() -> {
94-
Path outputFile = Paths.get(this.outputFolder + "/" + TEST_FILE_NAME);
96+
Path outputFile = Paths.get(uniqueDirectory + "/" + TEST_FILE);
9597
assertThat(Files.exists(outputFile)).isTrue();
9698
assertThat(Files.isRegularFile(outputFile)).isTrue();
9799

@@ -104,23 +106,39 @@ void testFilePropagatedToLocalDirectory() {
104106
.iterateAll()
105107
.forEach(b -> blobNamesInOutputBucket.add(b.getName()));
106108

107-
assertThat(blobNamesInOutputBucket).contains(TEST_FILE_NAME);
109+
assertThat(blobNamesInOutputBucket).contains(TEST_FILE);
108110
});
109111
}
110112

111113
void cleanupCloudStorage() {
112-
Page<Blob> blobs = this.storage.list(this.cloudInputBucket);
113-
for (Blob blob : blobs.iterateAll()) {
114-
blob.delete();
114+
BlobId inputBucketBlobId = BlobId.of(cloudInputBucket, TEST_FILE);
115+
Blob inputBucketBlob = storage.get(inputBucketBlobId);
116+
if (inputBucketBlob != null) {
117+
inputBucketBlob.delete();
118+
}
119+
120+
BlobId outputBucketBlobId = BlobId.of(cloudOutputBucket, TEST_FILE);
121+
Blob outputBucketBlob = storage.get(outputBucketBlobId);
122+
if (outputBucketBlob != null) {
123+
outputBucketBlob.delete();
115124
}
116125
}
117126

118-
void cleanupLocalDirectory() throws IOException {
119-
Path localDirectory = Paths.get(this.outputFolder);
120-
List<Path> files = Files.list(localDirectory).collect(Collectors.toList());
121-
for (Path file : files) {
122-
Files.delete(file);
127+
void cleanupLocalDirectory(Path testDirectory) throws IOException {
128+
if (Files.exists(testDirectory)) {
129+
if (Files.isDirectory(testDirectory)) {
130+
try (Stream<Path> files = Files.list(testDirectory)) {
131+
files.forEach(
132+
path -> {
133+
try {
134+
Files.delete(path);
135+
} catch (IOException ioe) {
136+
LOGGER.info("Error deleting test file.", ioe);
137+
}
138+
});
139+
}
140+
}
141+
Files.delete(testDirectory);
123142
}
124-
Files.deleteIfExists(Paths.get(this.outputFolder));
125143
}
126144
}

spring-cloud-gcp-samples/spring-cloud-gcp-storage-resource-sample/src/main/java/com/example/GcsApplication.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.springframework.boot.SpringApplication;
2020
import org.springframework.boot.autoconfigure.SpringBootApplication;
2121

22+
2223
/**
2324
* An example Spring Boot application that reads and writes files stored in Google Cloud Storage
2425
* (GCS) using the Spring Resource abstraction and the gs: protocol prefix.

spring-cloud-gcp-samples/spring-cloud-gcp-storage-resource-sample/src/main/java/com/example/WebController.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616

1717
package com.example;
1818

19+
import com.google.cloud.spring.storage.GoogleStorageResource;
20+
import com.google.cloud.storage.Storage;
1921
import java.io.IOException;
2022
import java.io.OutputStream;
2123
import java.nio.charset.Charset;
24+
import java.util.Optional;
2225
import org.springframework.beans.factory.annotation.Value;
2326
import org.springframework.core.io.Resource;
2427
import org.springframework.core.io.WritableResource;
2528
import org.springframework.util.StreamUtils;
2629
import org.springframework.web.bind.annotation.GetMapping;
2730
import org.springframework.web.bind.annotation.PostMapping;
2831
import org.springframework.web.bind.annotation.RequestBody;
32+
import org.springframework.web.bind.annotation.RequestParam;
2933
import org.springframework.web.bind.annotation.RestController;
3034

3135
/**
@@ -35,19 +39,46 @@
3539
@RestController
3640
public class WebController {
3741

42+
@Value("${gcs-resource-test-bucket}")
43+
private String bucketName;
44+
3845
@Value("gs://${gcs-resource-test-bucket}/my-file.txt")
3946
private Resource gcsFile;
4047

41-
@GetMapping("/")
42-
public String readGcsFile() throws IOException {
43-
return StreamUtils.copyToString(this.gcsFile.getInputStream(), Charset.defaultCharset()) + "\n";
48+
private Storage storage;
49+
50+
private WebController(Storage storage) {
51+
this.storage = storage;
52+
}
53+
54+
@GetMapping(value = "/")
55+
public String readGcsFile(@RequestParam("filename") Optional<String> filename)
56+
throws IOException {
57+
return StreamUtils.copyToString(
58+
filename.isPresent()
59+
? fetchResource(filename.get()).getInputStream()
60+
: this.gcsFile.getInputStream(),
61+
Charset.defaultCharset())
62+
+ "\n";
4463
}
4564

46-
@PostMapping("/")
47-
public String writeGcs(@RequestBody String data) throws IOException {
48-
try (OutputStream os = ((WritableResource) this.gcsFile).getOutputStream()) {
65+
@PostMapping(value = "/")
66+
public String writeGcs(
67+
@RequestBody String data, @RequestParam("filename") Optional<String> filename)
68+
throws IOException {
69+
return updateResource(
70+
filename.map(this::fetchResource).orElse((GoogleStorageResource) this.gcsFile), data);
71+
}
72+
73+
private String updateResource(Resource resource, String data) throws IOException {
74+
try (OutputStream os = ((WritableResource) resource).getOutputStream()) {
4975
os.write(data.getBytes());
5076
}
5177
return "file was updated\n";
5278
}
79+
80+
private GoogleStorageResource fetchResource(String filename) {
81+
return new GoogleStorageResource(
82+
this.storage, String.format("gs://%s/%s", this.bucketName, filename));
83+
}
5384
}

0 commit comments

Comments
 (0)