Skip to content

Commit 889651e

Browse files
authored
chore: add native image sample for google-cloud-secretmanager (#10805)
* chore: add native image sample for google-cloud-secretmanager
1 parent 530c787 commit 889651e

File tree

9 files changed

+465
-2
lines changed

9 files changed

+465
-2
lines changed

generate-readme.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ def client_for_module(module) -> Optional[CloudClient]:
115115
'java-grafeas',
116116
'java-notification',
117117
'java-shared-config',
118-
'java-shared-dependencies'
118+
'java-shared-dependencies',
119+
'java-samples'
119120
]
120121

121122
LIBRARIES_IN_MONOREPO = glob("java-*")

generation/check_non_release_please_versions.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ for pomFile in $(find . -mindepth 2 -name pom.xml | sort ); do
1515
# Skip the template files
1616
continue
1717
fi
18+
if [[ "${pomFile}" =~ .*java-samples.* ]]; then
19+
echo "Skipping version check for java-samples directory"
20+
continue
21+
fi
1822

1923
if grep -n '<version>.*</version>' "$pomFile" | grep -v 'x-version-update'; then
2024
echo "Found version declaration(s) without x-version-update in: $pomFile"

generation/consolidate_config.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ function runRegexOnPoms {
1212
for pomFile in $(find . -mindepth 2 -maxdepth 3 -name pom.xml |sort --dictionary-order); do
1313
if [[ $pomFile =~ .*google-cloud-jar-parent.* ]] || \
1414
[[ $pomFile =~ .*google-cloud-pom-parent.* ]] || \
15-
[[ $pomFile =~ .*java-shared-dependencies.* ]]; then
15+
[[ $pomFile =~ .*java-shared-dependencies.* ]] || \
16+
[[ $pomFile =~ .*java-samples.* ]]; then
1617
continue
1718
fi
1819

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Secret Manager Sample Application with Native Image
2+
3+
The Secret Manager sample application demonstrates some common operations with [Google Cloud Secret Manager](https://cloud.google.com/secret-manager) and is compatible with Native Image compilation.
4+
5+
This application will create a new secret named `native-secretmanager-test-secret` if it does not already exist.
6+
It will then add a new version of the secret and then attempt to read it.
7+
8+
## Setup Instructions
9+
10+
You will need to follow these prerequisite steps in order to run these samples:
11+
12+
1. If you have not already, [create a Google Cloud Platform Project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project).
13+
14+
2. Install the [Google Cloud SDK](https://cloud.google.com/sdk/) which will allow you to run the sample with your project's credentials.
15+
16+
Once installed, log in with Application Default Credentials using the following command:
17+
18+
```
19+
gcloud auth application-default login
20+
```
21+
22+
**Note:** Authenticating with Application Default Credentials is convenient to use during development, but we recommend [alternate methods of authentication](https://cloud.google.com/docs/authentication/production) during production use.
23+
24+
3. Install the native image compiler.
25+
26+
You can follow the [official installation instructions](https://www.graalvm.org/docs/getting-started/#install-graalvm).
27+
After following the instructions, ensure that you install the Native Image extension installed by running:
28+
29+
```
30+
gu install native-image
31+
```
32+
33+
4. [Enable the Secret Manager APIs](https://console.cloud.google.com/apis/api/secretmanager.googleapis.com).
34+
35+
### Run with Native Image Compilation
36+
37+
Navigate to this directory in a new terminal.
38+
39+
1. Compile the application using the native image compiler. This step may take a few minutes.
40+
41+
```
42+
mvn package -P native -DskipTests
43+
```
44+
45+
2. Run the application:
46+
47+
```
48+
./target/native-image-sample
49+
```
50+
51+
3. The application runs through some basic Secret Manager operations (create, update, read) and then prints some results of the operations.
52+
53+
```
54+
Created secret: projects/xxxxxx/secrets/graal-secretmanager-test-secret
55+
Added Secret Version: projects/xxxxxx/secrets/graal-secretmanager-test-secret/versions/1
56+
Reading secret value: Hello World
57+
(Note: Don't print secret values in prod!)
58+
```
59+
60+
## Sample Integration test with Native Image Support
61+
62+
In order to run the sample integration test as a native image, call the following command:
63+
64+
```
65+
mvn test -Pnative
66+
```
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
6+
<parent>
7+
<artifactId>google-cloud-samples</artifactId>
8+
<groupId>com.google.cloud</groupId>
9+
<version>0.1.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<modelVersion>4.0.0</modelVersion>
13+
<artifactId>native-image-sample</artifactId>
14+
<name>Native Image Sample</name>
15+
16+
<dependencyManagement>
17+
<dependencies>
18+
<dependency>
19+
<groupId>com.google.cloud</groupId>
20+
<artifactId>libraries-bom</artifactId>
21+
<version>26.38.0</version>
22+
<type>pom</type>
23+
<scope>import</scope>
24+
</dependency>
25+
</dependencies>
26+
</dependencyManagement>
27+
28+
<dependencies>
29+
<dependency>
30+
<groupId>com.google.cloud</groupId>
31+
<artifactId>google-cloud-core</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>com.google.cloud</groupId>
35+
<artifactId>google-cloud-secretmanager</artifactId>
36+
</dependency>
37+
</dependencies>
38+
39+
<build>
40+
<plugins>
41+
<plugin>
42+
<groupId>org.apache.maven.plugins</groupId>
43+
<artifactId>maven-deploy-plugin</artifactId>
44+
<version>3.1.2</version>
45+
<configuration>
46+
<skip>true</skip>
47+
</configuration>
48+
</plugin>
49+
50+
<!-- This plugin enables building the application to a JAR *not* using native image compilation -->
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-jar-plugin</artifactId>
54+
<version>3.3.0</version>
55+
<configuration>
56+
<archive>
57+
<manifest>
58+
<addClasspath>true</addClasspath>
59+
<classpathPrefix>dependency-jars/</classpathPrefix>
60+
<mainClass>com.google.cloud.example.NativeImageSecretManagerSample</mainClass>
61+
</manifest>
62+
</archive>
63+
</configuration>
64+
</plugin>
65+
<plugin>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-dependency-plugin</artifactId>
68+
<version>3.3.0</version>
69+
<executions>
70+
<execution>
71+
<id>copy-dependencies</id>
72+
<phase>package</phase>
73+
<goals>
74+
<goal>copy-dependencies</goal>
75+
</goals>
76+
<configuration>
77+
<outputDirectory>
78+
${project.build.directory}/dependency-jars/
79+
</outputDirectory>
80+
</configuration>
81+
</execution>
82+
</executions>
83+
</plugin>
84+
</plugins>
85+
</build>
86+
87+
<profiles>
88+
<!-- Native Profile-->
89+
<!-- TODO(10840): Use maven properties to track versions once https://github.com/googleapis/java-shared-config/pull/824 is merged. -->
90+
<profile>
91+
<id>native</id>
92+
93+
<dependencies>
94+
<dependency>
95+
<groupId>org.opentest4j</groupId>
96+
<artifactId>opentest4j</artifactId>
97+
<version>1.3.0</version>
98+
</dependency>
99+
<dependency>
100+
<groupId>org.junit.vintage</groupId>
101+
<artifactId>junit-vintage-engine</artifactId>
102+
<version>5.10.2</version>
103+
<scope>test</scope>
104+
</dependency>
105+
</dependencies>
106+
107+
<build>
108+
<plugins>
109+
<plugin>
110+
<groupId>org.apache.maven.plugins</groupId>
111+
<artifactId>maven-surefire-plugin</artifactId>
112+
<version>3.2.5</version>
113+
<dependencies>
114+
<dependency>
115+
<groupId>org.junit.vintage</groupId>
116+
<artifactId>junit-vintage-engine</artifactId>
117+
<version>5.10.2</version>
118+
</dependency>
119+
</dependencies>
120+
<configuration>
121+
<excludes combine.self="override" />
122+
<includes>
123+
<include>**/IT*.java</include>
124+
</includes>
125+
</configuration>
126+
</plugin>
127+
<plugin>
128+
<groupId>org.graalvm.buildtools</groupId>
129+
<artifactId>native-maven-plugin</artifactId>
130+
<version>0.10.1</version>
131+
<extensions>true</extensions>
132+
<configuration>
133+
<mainClass>com.google.cloud.example.NativeImageSecretManagerSample</mainClass>
134+
<buildArgs>
135+
<buildArg>--no-fallback</buildArg>
136+
</buildArgs>
137+
</configuration>
138+
<executions>
139+
140+
<!-- Configuration to build sample with native image compilation -->
141+
<execution>
142+
<id>build-native</id>
143+
<goals>
144+
<goal>build</goal>
145+
<goal>test</goal>
146+
</goals>
147+
<phase>package</phase>
148+
</execution>
149+
150+
<!-- Configuration to run ITNativeImageSecretManager with native image compilation -->
151+
<execution>
152+
<id>test-native</id>
153+
<goals>
154+
<goal>test</goal>
155+
</goals>
156+
<phase>test</phase>
157+
</execution>
158+
</executions>
159+
</plugin>
160+
</plugins>
161+
</build>
162+
</profile>
163+
</profiles>
164+
</project>
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2024 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.google.cloud.example;
18+
19+
import com.google.cloud.ServiceOptions;
20+
import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
21+
import com.google.cloud.secretmanager.v1.ProjectName;
22+
import com.google.cloud.secretmanager.v1.Replication;
23+
import com.google.cloud.secretmanager.v1.Secret;
24+
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
25+
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient.ListSecretsPagedResponse;
26+
import com.google.cloud.secretmanager.v1.SecretName;
27+
import com.google.cloud.secretmanager.v1.SecretPayload;
28+
import com.google.cloud.secretmanager.v1.SecretVersion;
29+
import com.google.protobuf.ByteString;
30+
import java.io.IOException;
31+
32+
/**
33+
* Sample application demonstrating Native Image compatibility with Google Cloud Secret Manager
34+
* APIs.
35+
*/
36+
public class NativeImageSecretManagerSample {
37+
38+
public static void main(String[] args) throws IOException {
39+
String secretId = "native-secretmanager-test-secret";
40+
String projectId = ServiceOptions.getDefaultProjectId();
41+
42+
try (SecretManagerServiceClient client = SecretManagerServiceClient.create()) {
43+
if (!hasSecret(client, projectId, secretId)) {
44+
createSecret(client, projectId, secretId);
45+
} else {
46+
System.out.println("Project already has secret: " + secretId);
47+
}
48+
49+
SecretVersion version = addSecretVersion(client, projectId, secretId);
50+
printSecretVersion(client, version);
51+
}
52+
}
53+
54+
static void createSecret(SecretManagerServiceClient client, String projectId, String secretId) {
55+
Secret secret =
56+
Secret.newBuilder()
57+
.setReplication(
58+
Replication.newBuilder()
59+
.setAutomatic(Replication.Automatic.newBuilder().build())
60+
.build())
61+
.build();
62+
ProjectName projectName = ProjectName.of(projectId);
63+
Secret createdSecret = client.createSecret(projectName, secretId, secret);
64+
System.out.println("Created secret: " + createdSecret.getName());
65+
}
66+
67+
static boolean hasSecret(SecretManagerServiceClient client, String projectId, String secretId) {
68+
69+
ProjectName projectName = ProjectName.of(projectId);
70+
ListSecretsPagedResponse pagedResponse = client.listSecrets(projectName);
71+
72+
for (Secret secret : pagedResponse.iterateAll()) {
73+
String otherSecretId = extractSecretId(secret);
74+
if (secretId.equals(otherSecretId)) {
75+
return true;
76+
}
77+
}
78+
79+
return false;
80+
}
81+
82+
static SecretVersion addSecretVersion(
83+
SecretManagerServiceClient client, String projectId, String secretId) {
84+
85+
SecretName secretName = SecretName.of(projectId, secretId);
86+
SecretPayload payload =
87+
SecretPayload.newBuilder().setData(ByteString.copyFromUtf8("Hello World")).build();
88+
89+
SecretVersion version = client.addSecretVersion(secretName, payload);
90+
System.out.println("Added Secret Version: " + version.getName());
91+
return version;
92+
}
93+
94+
static void printSecretVersion(SecretManagerServiceClient client, SecretVersion version) {
95+
AccessSecretVersionResponse response = client.accessSecretVersion(version.getName());
96+
String payload = response.getPayload().getData().toStringUtf8();
97+
System.out.println("Reading secret value: " + payload);
98+
System.out.println("(Note: Don't print secret values in prod!)");
99+
}
100+
101+
/**
102+
* Returns the secret ID from the fully-qualified secret name which has the format:
103+
* projects/YOUR_PROJECT_ID/secrets/YOUR_SECRET_ID.
104+
*
105+
* @param secret {@link Secret} to extract id from.
106+
* @return a {@link String} representing the secret id
107+
*/
108+
private static String extractSecretId(Secret secret) {
109+
String[] secretNameTokens = secret.getName().split("/");
110+
return secretNameTokens[secretNameTokens.length - 1];
111+
}
112+
}

0 commit comments

Comments
 (0)