How to get the project ID in a Java Cloud Function?

As I was working with my colleague Sara Ford on testing the Cloud Functions runtimes for the upcoming “second generation” of the product, rebased on the Cloud Run platform, I wrote a few simple functions for the Java runtime. In one of those Java functions, I wanted to use Google Cloud Storage, to download a file from a bucket. I took a look at the existing sample to download an object:

Storage storage = StorageOptions.newBuilder()
.setProjectId(projectId)
.build()
.getService();
Blob blob = storage.get(BlobId.of(bucketName, objectName));
blob.downloadTo(Paths.get(destFilePath));

I know the name of the bucket, the name of the file, I’m going to store the file in the local file system. So I have all the information needed… except the project ID within which I deployed my Java cloud function. So how do I get the project ID, in Java, inside the Cloud Functions environment?

A previous iteration of Cloud Functions had various useful environment variables available, which included the project ID. So you could retrieve the ID with a System.getenv() call. However, for various compatibility reasons between the various runtimes, with the open source Knative project, that variable disappeared along the road.

However, I know that the project ID is also part of the internal compute metadata that is accessible via a special URL:

http://metadata.google.internal/computeMetadata/v1/project/project-id

With that knowledge in mind, I thought I could simply make a quick HTTP request to get that information:

private String getProjectId() { 
String projectId = null;
HttpURLConnection conn = null;
try {
URL url = new URL("http://metadata.google.internal/computeMetadata/v1/project/project-id");
conn = (HttpURLConnection)(url.openConnection());
conn.setRequestProperty("Metadata-Flavor", "Google");
projectId = new String(conn.getInputStream().readAllBytes(),
StandardCharsets.UTF_8);
conn.disconnect();
} catch (Throwable t) {}
return projectId;
}

For the call to work, it is mandatory to set the Metadata-Flavor header that you see above. I used Java’s built-in HttpURLConnection for the job. There are other HTTP libraries that could’ve made the code simpler, but at first, I didn’t want to bring another HTTP client, just for retrieving a simple project meta-information.

I’m one of the developers who designed the Functions Framework for Java that is used to craft cloud functions in Java, however, I’ve written quite a few functions using Node.js as well. And in the Node ecosystem, there’s actually an NPM module whose responsibility is to retrieve such project metadata. With the gcp-metadata module, you can require it and then fetch the project ID with:

const gcpMetadata = require('gcp-metadata');
const projectId = await gcpMetadata.project('project-id');

I was surprised I couldn’t easily find an equivalent library in Java. It took me a while to find it, but it actually exists too! That’s the com.google.cloud:google-cloud-core library! And it’s trivial to use:

import com.google.cloud.ServiceOptions;String projectId = ServiceOptions.getDefaultProjectId();

An extra dependency in my pom.xml , one import and one static method call on ServiceOptions, and I can get the GCP project ID! So I’m now able to pass the project ID to my StorageOptions builder.

But for some reason, I recalled that at times, in some other projects I had written, I remembered not really needing that project ID information, as the libraries I was using were smart enough to infer such information from the environment. Let’s look again at the StorageOptions from the beginning. What if I simply omit the setProjectId() method call? Lo and behold… indeed, it was actually not required, and the project ID was inferred, transparently. So I didn’t really need to search for how to retrieve this project ID at all! And actually, you can further simplify the creation of the StorageOptions down to:

Storage storage = StorageOptions
.getDefaultInstance()
.getService();

At least, now, I know how to retrieve the project ID in Java, in case the libraries or the environment are not providing such details on their own!

Originally published at https://glaforge.appspot.com.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Guillaume Laforge

Guillaume Laforge

Developer Advocate for Google Cloud Platform, Apache Groovy programming language project VP/Chair