10

I have an API that is containerized and running inside cloud run. How can I get the current project ID where my cloud run is executing? I have tried:

  • I see it in textpayload in logs but I am not sure how to read the textpayload inside the post function? The pub sub message I receive is missing this information.
  • I have read up into querying the metadata api, but it is not very clear on how to do that again from within the api. Any links?

Is there any other way?

Edit:

After some comments below, I ended up with this code inside my .net API running inside Cloud Run.

        private string GetProjectid()
        {
            var projectid = string.Empty;
            try {
                var PATH = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Add("Metadata-Flavor", "Google");
                    projectid = client.GetStringAsync(PATH).Result.ToString();
                }

                Console.WriteLine("PROJECT: " + projectid);
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message + " --- " + ex.ToString());
            }
            return projectid;
        }

Update, it works. My build pushes had been failing and I did not see. Thanks everyone.

Liam
  • 27,717
  • 28
  • 128
  • 190
AIK DO
  • 288
  • 1
  • 4
  • 13
  • 4
    Make an HTTP GET request to `http://metadata.google.internal/computeMetadata/v1/project/project-id` – John Hanley Dec 19 '19 at 21:29
  • I get (Response status code does not indicate success: 403 (Forbidden when trying to do this. – AIK DO Dec 23 '19 at 15:15
  • 1
    Where are you making the request from? What scopes are enabled for your Compute Engine instance? What HTTP headers did you add? See Steren's answer for making requests to the Metadata server which must include the `Metadata-Flavor:Google` header. – John Hanley Dec 23 '19 at 15:28
  • I did not! How do I get the ACCESS_TOKEN? – AIK DO Dec 23 '19 at 15:40
  • 2
    I changed my comment as the metadata server is only available when your code is running inside a Compute Engine instance and does not require an Access Token. If you are running your code somewhere else (a web browser, for example), you cannot call this endpoint. Edit your question with the exact details on how you are calling this endpoint and include the error messages. – John Hanley Dec 23 '19 at 15:44

7 Answers7

7

You get the project ID by sending an GET request to http://metadata.google.internal/computeMetadata/v1/project/project-id with the Metadata-Flavor:Google header.

See this documentation

In Node.js for example:

index.js:

    const express = require('express');
    const axios = require('axios');
    const app = express();
    
    const axiosInstance = axios.create({
      baseURL: 'http://metadata.google.internal/',
      timeout: 1000,
      headers: {'Metadata-Flavor': 'Google'}
    });
    
    app.get('/', (req, res) => {
      let path = req.query.path || 'computeMetadata/v1/project/project-id';
      axiosInstance.get(path).then(response => {
        console.log(response.status)
        console.log(response.data);
        res.send(response.data);
      });
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log('Hello world listening on port', port);
    });

package.json:


    {
      "name": "metadata",
      "version": "1.0.0",
      "description": "Metadata server",
      "main": "app.js",
      "scripts": {
        "start": "node index.js"
      },
      "author": "",
      "license": "Apache-2.0",
      "dependencies": {
        "axios": "^0.18.0",
        "express": "^4.16.4"
      }
    }
Liam
  • 27,717
  • 28
  • 128
  • 190
Steren
  • 7,311
  • 3
  • 31
  • 51
  • Here is a [list of all of the urls to obtain various pieces of information from this service](https://cloud.google.com/appengine/docs/legacy/standard/java/accessing-instance-metadata) e.g. `zone`, etc. – Liam Aug 09 '22 at 15:05
  • In fact you don't have to write the HTTP yourself, there is a [gcp meta data client available](https://github.com/googleapis/gcp-metadata) – Liam Aug 10 '22 at 08:11
4

Others have shown how to get the project name via HTTP API, but in my opinion the easier, simpler, and more performant thing to do here is to just set the project ID as a run-time environment variable. To do this, when you deploy the function:

gcloud functions deploy myFunction --set-env-vars PROJECT_ID=my-project-name

And then you would access it in code like:

exports.myFunction = (req, res) => {
  console.log(process.env.PROJECT_ID);
}

You would simply need to set the proper value for each environment where you deploy the function. This has the very minor downside of requiring a one-time command line parameter for each environment, and the very major upside of not making your function depend on successfully authenticating with and parsing an API response. This also provides code portability, because virtually all hosting environments support environment variables, including your local development environment.

Matt Korostoff
  • 1,927
  • 2
  • 21
  • 26
  • Some older runtimes (e.g. nodejs8) automatically populated `process.env. GCP_PROJECT` but this has been deprecated for a while as of this writing – Matt Korostoff Oct 15 '21 at 17:48
  • i think this one can be a good alternative to getting the project_id from meta server, which may not be presense when developing locally. – Tom Tang May 04 '22 at 03:46
4

@Steren 's answer in python

import os

def get_project_id():
    # In python 3.7, this works
    project_id = os.getenv("GCP_PROJECT")

    if not project_id:  # > python37
        # Only works on runtime.
        import urllib.request

        url = "http://metadata.google.internal/computeMetadata/v1/project/project-id"
        req = urllib.request.Request(url)
        req.add_header("Metadata-Flavor", "Google")
        project_id = urllib.request.urlopen(req).read().decode()

    if not project_id:  # Running locally
        with open(os.environ["GOOGLE_APPLICATION_CREDENTIALS"], "r") as fp:
            credentials = json.load(fp)
        project_id = credentials["project_id"]

    if not project_id:
        raise ValueError("Could not get a value for PROJECT_ID")

    return project_id
ignorant
  • 1,390
  • 1
  • 10
  • 14
  • 1
    This also works for Google AppEngine, except the environment variable is called GOOGLE_CLOUD_PROJECT instead of GCP_PROJECT. – Codo Mar 09 '23 at 16:38
2

official Google's client library:

import gcpMetadata from 'gcp-metadata'

const projectId = await gcpMetadata.project('project-id')
vitaliytv
  • 694
  • 7
  • 9
0
  1. I followed the tutorial Using Pub/Sub with Cloud Run tutorial

  2. I added to the requirements.txt the module gcloud

       Flask==1.1.1
       pytest==5.3.0; python_version > "3.0"
       pytest==4.6.6; python_version < "3.0"
       gunicorn==19.9.0
       gcloud
    
  3. I changed index function in main.py:

       def index():
         envelope = request.get_json()
         if not envelope:
            msg = 'no Pub/Sub message received'
            print(f'error: {msg}')
            return f'Bad Request: {msg}', 400
         if not isinstance(envelope, dict) or 'message' not in envelope:
            msg = 'invalid Pub/Sub message format'
            print(f'error: {msg}')
            return f'Bad Request: {msg}', 400
         pubsub_message = envelope['message']
         name = 'World'
         if isinstance(pubsub_message, dict) and 'data' in pubsub_message:
            name = base64.b64decode(pubsub_message['data']).decode('utf-8').strip()
         print(f'Hello {name}!')
    
         #code added 
         from gcloud import pubsub  # Or whichever service you need
         client = pubsub.Client()
         print('This is the project {}'.format(client.project))
    
         # Flush the stdout to avoid log buffering.
         sys.stdout.flush()
         return ('', 204)
    
    1. I checked the logs:

        Hello (pubsub message).
        This is the project my-project-id.
      
marian.vladoi
  • 7,663
  • 1
  • 15
  • 29
0

Here is a snippet of Java code that fetches the current project ID:

            String url = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
            HttpURLConnection conn = (HttpURLConnection)(new URL(url).openConnection());
            conn.setRequestProperty("Metadata-Flavor", "Google");
            try {
                InputStream in = conn.getInputStream();
                projectId = new String(in.readAllBytes(), StandardCharsets.UTF_8);
            } finally {
                conn.disconnect();
            }               
cayhorstmann
  • 3,192
  • 1
  • 25
  • 17
-1

It should be possible to use the Platform class from Google.Api.Gax (https://github.com/googleapis/gax-dotnet/blob/master/Google.Api.Gax/Platform.cs). The Google.Api.Gax package is usually installed as dependency for the other Google .NET packages like Google.Cloud.Storage.V1

var projectId = Google.Api.Gax.Platform.Instance().ProjectId;

On the GAE platform, you can also simply check environment variables GOOGLE_CLOUD_PROJECT and GCLOUD_PROJECT

var projectId = Environment.GetEnvironmentVariable("GOOGLE_CLOUD_PROJECT")
             ?? Environment.GetEnvironmentVariable("GCLOUD_PROJECT");
Oldrich Dlouhy
  • 597
  • 6
  • 6