Skip to content

Commit

Permalink
chore: add native image sample for google-cloud-secretmanager (#10805)
Browse files Browse the repository at this point in the history
* chore: add native image sample for google-cloud-secretmanager
  • Loading branch information
mpeddada1 authored May 16, 2024
1 parent 530c787 commit 889651e
Show file tree
Hide file tree
Showing 9 changed files with 465 additions and 2 deletions.
3 changes: 2 additions & 1 deletion generate-readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ def client_for_module(module) -> Optional[CloudClient]:
'java-grafeas',
'java-notification',
'java-shared-config',
'java-shared-dependencies'
'java-shared-dependencies',
'java-samples'
]

LIBRARIES_IN_MONOREPO = glob("java-*")
Expand Down
4 changes: 4 additions & 0 deletions generation/check_non_release_please_versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ for pomFile in $(find . -mindepth 2 -name pom.xml | sort ); do
# Skip the template files
continue
fi
if [[ "${pomFile}" =~ .*java-samples.* ]]; then
echo "Skipping version check for java-samples directory"
continue
fi

if grep -n '<version>.*</version>' "$pomFile" | grep -v 'x-version-update'; then
echo "Found version declaration(s) without x-version-update in: $pomFile"
Expand Down
3 changes: 2 additions & 1 deletion generation/consolidate_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ function runRegexOnPoms {
for pomFile in $(find . -mindepth 2 -maxdepth 3 -name pom.xml |sort --dictionary-order); do
if [[ $pomFile =~ .*google-cloud-jar-parent.* ]] || \
[[ $pomFile =~ .*google-cloud-pom-parent.* ]] || \
[[ $pomFile =~ .*java-shared-dependencies.* ]]; then
[[ $pomFile =~ .*java-shared-dependencies.* ]] || \
[[ $pomFile =~ .*java-samples.* ]]; then
continue
fi

Expand Down
66 changes: 66 additions & 0 deletions java-samples/native-image-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Secret Manager Sample Application with Native Image

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.

This application will create a new secret named `native-secretmanager-test-secret` if it does not already exist.
It will then add a new version of the secret and then attempt to read it.

## Setup Instructions

You will need to follow these prerequisite steps in order to run these samples:

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).

2. Install the [Google Cloud SDK](https://cloud.google.com/sdk/) which will allow you to run the sample with your project's credentials.

Once installed, log in with Application Default Credentials using the following command:

```
gcloud auth application-default login
```
**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.
3. Install the native image compiler.
You can follow the [official installation instructions](https://www.graalvm.org/docs/getting-started/#install-graalvm).
After following the instructions, ensure that you install the Native Image extension installed by running:
```
gu install native-image
```
4. [Enable the Secret Manager APIs](https://console.cloud.google.com/apis/api/secretmanager.googleapis.com).
### Run with Native Image Compilation
Navigate to this directory in a new terminal.
1. Compile the application using the native image compiler. This step may take a few minutes.
```
mvn package -P native -DskipTests
```
2. Run the application:
```
./target/native-image-sample
```
3. The application runs through some basic Secret Manager operations (create, update, read) and then prints some results of the operations.
```
Created secret: projects/xxxxxx/secrets/graal-secretmanager-test-secret
Added Secret Version: projects/xxxxxx/secrets/graal-secretmanager-test-secret/versions/1
Reading secret value: Hello World
(Note: Don't print secret values in prod!)
```
## Sample Integration test with Native Image Support
In order to run the sample integration test as a native image, call the following command:
```
mvn test -Pnative
```
164 changes: 164 additions & 0 deletions java-samples/native-image-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<artifactId>google-cloud-samples</artifactId>
<groupId>com.google.cloud</groupId>
<version>0.1.0-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<artifactId>native-image-sample</artifactId>
<name>Native Image Sample</name>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.38.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-secretmanager</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>

<!-- This plugin enables building the application to a JAR *not* using native image compilation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>dependency-jars/</classpathPrefix>
<mainClass>com.google.cloud.example.NativeImageSecretManagerSample</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/dependency-jars/
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>
<!-- Native Profile-->
<!-- TODO(10840): Use maven properties to track versions once https://github.com/googleapis/java-shared-config/pull/824 is merged. -->
<profile>
<id>native</id>

<dependencies>
<dependency>
<groupId>org.opentest4j</groupId>
<artifactId>opentest4j</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<dependencies>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.10.2</version>
</dependency>
</dependencies>
<configuration>
<excludes combine.self="override" />
<includes>
<include>**/IT*.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.10.1</version>
<extensions>true</extensions>
<configuration>
<mainClass>com.google.cloud.example.NativeImageSecretManagerSample</mainClass>
<buildArgs>
<buildArg>--no-fallback</buildArg>
</buildArgs>
</configuration>
<executions>

<!-- Configuration to build sample with native image compilation -->
<execution>
<id>build-native</id>
<goals>
<goal>build</goal>
<goal>test</goal>
</goals>
<phase>package</phase>
</execution>

<!-- Configuration to run ITNativeImageSecretManager with native image compilation -->
<execution>
<id>test-native</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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
*
* https://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.example;

import com.google.cloud.ServiceOptions;
import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1.ProjectName;
import com.google.cloud.secretmanager.v1.Replication;
import com.google.cloud.secretmanager.v1.Secret;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient.ListSecretsPagedResponse;
import com.google.cloud.secretmanager.v1.SecretName;
import com.google.cloud.secretmanager.v1.SecretPayload;
import com.google.cloud.secretmanager.v1.SecretVersion;
import com.google.protobuf.ByteString;
import java.io.IOException;

/**
* Sample application demonstrating Native Image compatibility with Google Cloud Secret Manager
* APIs.
*/
public class NativeImageSecretManagerSample {

public static void main(String[] args) throws IOException {
String secretId = "native-secretmanager-test-secret";
String projectId = ServiceOptions.getDefaultProjectId();

try (SecretManagerServiceClient client = SecretManagerServiceClient.create()) {
if (!hasSecret(client, projectId, secretId)) {
createSecret(client, projectId, secretId);
} else {
System.out.println("Project already has secret: " + secretId);
}

SecretVersion version = addSecretVersion(client, projectId, secretId);
printSecretVersion(client, version);
}
}

static void createSecret(SecretManagerServiceClient client, String projectId, String secretId) {
Secret secret =
Secret.newBuilder()
.setReplication(
Replication.newBuilder()
.setAutomatic(Replication.Automatic.newBuilder().build())
.build())
.build();
ProjectName projectName = ProjectName.of(projectId);
Secret createdSecret = client.createSecret(projectName, secretId, secret);
System.out.println("Created secret: " + createdSecret.getName());
}

static boolean hasSecret(SecretManagerServiceClient client, String projectId, String secretId) {

ProjectName projectName = ProjectName.of(projectId);
ListSecretsPagedResponse pagedResponse = client.listSecrets(projectName);

for (Secret secret : pagedResponse.iterateAll()) {
String otherSecretId = extractSecretId(secret);
if (secretId.equals(otherSecretId)) {
return true;
}
}

return false;
}

static SecretVersion addSecretVersion(
SecretManagerServiceClient client, String projectId, String secretId) {

SecretName secretName = SecretName.of(projectId, secretId);
SecretPayload payload =
SecretPayload.newBuilder().setData(ByteString.copyFromUtf8("Hello World")).build();

SecretVersion version = client.addSecretVersion(secretName, payload);
System.out.println("Added Secret Version: " + version.getName());
return version;
}

static void printSecretVersion(SecretManagerServiceClient client, SecretVersion version) {
AccessSecretVersionResponse response = client.accessSecretVersion(version.getName());
String payload = response.getPayload().getData().toStringUtf8();
System.out.println("Reading secret value: " + payload);
System.out.println("(Note: Don't print secret values in prod!)");
}

/**
* Returns the secret ID from the fully-qualified secret name which has the format:
* projects/YOUR_PROJECT_ID/secrets/YOUR_SECRET_ID.
*
* @param secret {@link Secret} to extract id from.
* @return a {@link String} representing the secret id
*/
private static String extractSecretId(Secret secret) {
String[] secretNameTokens = secret.getName().split("/");
return secretNameTokens[secretNameTokens.length - 1];
}
}
Loading

0 comments on commit 889651e

Please sign in to comment.