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