Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get project ID from JSON credentials file #1813

Merged
60 changes: 60 additions & 0 deletions docs/gcloud-config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Configuration
*************

Overview
========

Use service client objects to configure your applications.

For example:

.. code-block:: python

>>> from gcloud import bigquery
>>> client = bigquery.Client()

When creating a client in this way, the project ID will be determined by
searching these locations in the following order.

* GCLOUD_PROJECT environment variable
* GOOGLE_APPLICATION_CREDENTIALS JSON file
* Default service configuration path from
``$ gcloud beta auth application-default login``.
* Google App Engine application ID
* Google Compute Engine project ID (from metadata server)

You can override the detection of your default project by setting the
``project`` parameter when creating client objects.

.. code-block:: python

>>> from gcloud import bigquery
>>> client = bigquery.Client(project='my-project')

You can see what project ID a client is referencing by accessing the ``project``
property on the client object.

.. code-block:: python

>>> client.project
u'my-project'

Authentication
==============

The authentication credentials can be implicitly determined from the
environment or directly. See :doc:`gcloud-auth`.

Logging in via ``gcloud beta auth application-default login`` will
automatically configure a JSON key file with your default project ID and
credentials.

Setting the ``GOOGLE_APPLICATION_CREDENTIALS`` and ``GCLOUD_PROJECT``
environment variables will override the automatically configured credentials.

You can change your default project ID to ``my-new-default-project`` by
using the ``gcloud`` CLI tool to change the configuration.

.. code-block:: bash

$ gcloud config set project my-new-default-project
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
:caption: gcloud

gcloud-api
gcloud-config
gcloud-auth

.. toctree::
Expand Down
77 changes: 63 additions & 14 deletions gcloud/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import calendar
import datetime
import json
import os
import re
import socket
Expand All @@ -27,8 +28,10 @@
from google.protobuf import timestamp_pb2
import six
from six.moves.http_client import HTTPConnection
from six.moves import configparser

from gcloud.environment_vars import PROJECT
from gcloud.environment_vars import CREDENTIALS

try:
from google.appengine.api import app_identity
Expand All @@ -48,6 +51,7 @@
(?P<nanos>\d{1,9}) # nanoseconds, maybe truncated
Z # Zulu
""", re.VERBOSE)
DEFAULT_CONFIGURATION_PATH = '~/.config/gcloud/configurations/config_default'

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.



class _LocalStack(Local):
Expand Down Expand Up @@ -129,13 +133,13 @@ def _ensure_tuple_or_list(arg_name, tuple_or_list):
This effectively reduces the iterable types allowed to a very short
whitelist: list and tuple.

:type arg_name: string
:type arg_name: str
:param arg_name: Name of argument to use in error message.

:type tuple_or_list: sequence of string
:type tuple_or_list: sequence of str
:param tuple_or_list: Sequence to be verified.

:rtype: list of string
:rtype: list of str
:returns: The ``tuple_or_list`` passed in cast to a ``list``.
:raises: class:`TypeError` if the ``tuple_or_list`` is not a tuple or
list.
Expand All @@ -149,7 +153,7 @@ def _ensure_tuple_or_list(arg_name, tuple_or_list):
def _app_engine_id():
"""Gets the App Engine application ID if it can be inferred.

:rtype: string or ``NoneType``
:rtype: str or ``NoneType``
:returns: App Engine application ID if running in App Engine,
else ``None``.
"""
Expand All @@ -159,6 +163,42 @@ def _app_engine_id():
return app_identity.get_application_id()


def _file_project_id():
"""Gets the project id from the credentials file if one is available.

:rtype: str or ``NoneType``
:returns: Project-ID from JSON credentials file if value exists,
else ``None``.
"""
credentials_file_path = os.getenv(CREDENTIALS)
if credentials_file_path:
with open(credentials_file_path, 'rb') as credentials_file:
credentials_json = credentials_file.read()
credentials = json.loads(credentials_json.decode('utf-8'))
return credentials.get('project_id')

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.



def _default_service_project_id():
"""Retrieves the project ID from the gcloud command line tool.

Files that cannot be opened with configparser are silently ignored; this is
designed so that you can specify a list of potential configuration file
locations.

:rtype: str or ``NoneType``
:returns: Project-ID from default configuration file else ``None``
"""
full_config_path = os.path.expanduser(DEFAULT_CONFIGURATION_PATH)
win32_config_path = os.path.join(os.getenv('APPDATA', ''),
'gcloud', 'configurations',
'config_default')
config = configparser.RawConfigParser()
config.read([full_config_path, win32_config_path])

if config.has_section('core'):
return config.get('core', 'project')


def _compute_engine_id():
"""Gets the Compute Engine project ID if it can be inferred.

Expand All @@ -172,7 +212,7 @@ def _compute_engine_id():
See https://github.com/google/oauth2client/issues/93 for context about
DNS latency.

:rtype: string or ``NoneType``
:rtype: str or ``NoneType``
:returns: Compute Engine project ID if the metadata service is available,
else ``None``.
"""
Expand Down Expand Up @@ -204,18 +244,27 @@ def _determine_default_project(project=None):
implicit environments are:

* GCLOUD_PROJECT environment variable
* GOOGLE_APPLICATION_CREDENTIALS JSON file
* Get default service project from
``$ gcloud beta auth application-default login``
* Google App Engine application ID
* Google Compute Engine project ID (from metadata server)

:type project: string
:type project: str
:param project: Optional. The project name to use as default.

:rtype: string or ``NoneType``
:rtype: str or ``NoneType``
:returns: Default project if it can be determined.
"""
if project is None:
project = _get_production_project()

if project is None:
project = _file_project_id()

This comment was marked as spam.


if project is None:
project = _default_service_project_id()

if project is None:
project = _app_engine_id()

Expand All @@ -231,7 +280,7 @@ def _millis(when):
:type when: :class:`datetime.datetime`
:param when: the datetime to convert

:rtype: integer
:rtype: int
:returns: milliseconds since epoch for ``when``
"""
micros = _microseconds_from_datetime(when)
Expand All @@ -256,7 +305,7 @@ def _microseconds_from_datetime(value):
:type value: :class:`datetime.datetime`
:param value: The timestamp to convert.

:rtype: integer
:rtype: int
:returns: The timestamp, in microseconds.
"""
if not value.tzinfo:
Expand All @@ -273,7 +322,7 @@ def _millis_from_datetime(value):
:type value: :class:`datetime.datetime`, or None
:param value: the timestamp

:rtype: integer, or ``NoneType``
:rtype: int, or ``NoneType``
:returns: the timestamp, in milliseconds, or None
"""
if value is not None:
Expand Down Expand Up @@ -430,20 +479,20 @@ def _datetime_to_pb_timestamp(when):
def _name_from_project_path(path, project, template):
"""Validate a URI path and get the leaf object's name.

:type path: string
:type path: str
:param path: URI path containing the name.

:type project: string or NoneType
:type project: str or NoneType
:param project: The project associated with the request. It is
included for validation purposes. If passed as None,
disables validation.

:type template: string
:type template: str
:param template: Template regex describing the expected form of the path.
The regex must have two named groups, 'project' and
'name'.

:rtype: string
:rtype: str

This comment was marked as spam.

:returns: Name parsed from ``path``.
:raises: :class:`ValueError` if the ``path`` is ill-formed or if
the project from the ``path`` does not agree with the
Expand Down
Loading