1# Copyright 2016 Google Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Provides helper methods for talking to the Compute Engine metadata server.
16
17See https://cloud.google.com/compute/docs/metadata
18"""
19
20import datetime
21import json
22import os
23
24from six.moves import http_client
25from six.moves.urllib import parse as urlparse
26
27from oauth2client import _helpers
28from oauth2client import client
29from oauth2client import transport
30
31
32METADATA_ROOT = 'http://{}/computeMetadata/v1/'.format(
33    os.getenv('GCE_METADATA_ROOT', 'metadata.google.internal'))
34METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
35
36
37def get(http, path, root=METADATA_ROOT, recursive=None):
38    """Fetch a resource from the metadata server.
39
40    Args:
41        http: an object to be used to make HTTP requests.
42        path: A string indicating the resource to retrieve. For example,
43            'instance/service-accounts/default'
44        root: A string indicating the full path to the metadata server root.
45        recursive: A boolean indicating whether to do a recursive query of
46            metadata. See
47            https://cloud.google.com/compute/docs/metadata#aggcontents
48
49    Returns:
50        A dictionary if the metadata server returns JSON, otherwise a string.
51
52    Raises:
53        http_client.HTTPException if an error corrured while
54        retrieving metadata.
55    """
56    url = urlparse.urljoin(root, path)
57    url = _helpers._add_query_parameter(url, 'recursive', recursive)
58
59    response, content = transport.request(
60        http, url, headers=METADATA_HEADERS)
61
62    if response.status == http_client.OK:
63        decoded = _helpers._from_bytes(content)
64        if response['content-type'] == 'application/json':
65            return json.loads(decoded)
66        else:
67            return decoded
68    else:
69        raise http_client.HTTPException(
70            'Failed to retrieve {0} from the Google Compute Engine'
71            'metadata service. Response:\n{1}'.format(url, response))
72
73
74def get_service_account_info(http, service_account='default'):
75    """Get information about a service account from the metadata server.
76
77    Args:
78        http: an object to be used to make HTTP requests.
79        service_account: An email specifying the service account for which to
80            look up information. Default will be information for the "default"
81            service account of the current compute engine instance.
82
83    Returns:
84         A dictionary with information about the specified service account,
85         for example:
86
87            {
88                'email': '...',
89                'scopes': ['scope', ...],
90                'aliases': ['default', '...']
91            }
92    """
93    return get(
94        http,
95        'instance/service-accounts/{0}/'.format(service_account),
96        recursive=True)
97
98
99def get_token(http, service_account='default'):
100    """Fetch an oauth token for the
101
102    Args:
103        http: an object to be used to make HTTP requests.
104        service_account: An email specifying the service account this token
105            should represent. Default will be a token for the "default" service
106            account of the current compute engine instance.
107
108    Returns:
109         A tuple of (access token, token expiration), where access token is the
110         access token as a string and token expiration is a datetime object
111         that indicates when the access token will expire.
112    """
113    token_json = get(
114        http,
115        'instance/service-accounts/{0}/token'.format(service_account))
116    token_expiry = client._UTCNOW() + datetime.timedelta(
117        seconds=token_json['expires_in'])
118    return token_json['access_token'], token_expiry
119