blob: fa0422ad2c2a9ab09520c2e6fda7aeee03f21492 [file] [log] [blame]
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -07001# Copyright 2016 Google Inc.
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"""Noxfile for automating system tests.
16
17This file handles setting up environments needed by the system tests. This
18separates the tests from their environment configuration.
19
20See the `nox docs`_ for details on how this file works:
21
22.. _nox docs: http://nox.readthedocs.io/en/latest/
23"""
24
25import os
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -070026import subprocess
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070027
28from nox.command import which
29import py.path
30
31
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -070032HERE = os.path.abspath(os.path.dirname(__file__))
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070033DATA_DIR = os.path.join(HERE, 'data')
34SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json')
35AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json')
36EXPLICIT_CREDENTIALS_ENV = 'GOOGLE_APPLICATION_CREDENTIALS'
37EXPLICIT_PROJECT_ENV = 'GOOGLE_CLOUD_PROJECT'
38EXPECT_PROJECT_ENV = 'EXPECT_PROJECT_ID'
39
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -070040SKIP_GAE_TEST_ENV = 'SKIP_APP_ENGINE_SYSTEM_TEST'
41GAE_APP_URL_TMPL = 'https://{}-dot-{}.appspot.com'
42GAE_TEST_APP_SERVICE = 'google-auth-system-tests'
43
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070044# The download location for the Cloud SDK
45CLOUD_SDK_DIST_FILENAME = 'google-cloud-sdk.tar.gz'
46CLOUD_SDK_DOWNLOAD_URL = (
47 'https://dl.google.com/dl/cloudsdk/release/{}'.format(
48 CLOUD_SDK_DIST_FILENAME))
49
50# This environment variable is recognized by the Cloud SDK and overrides
51# the location of the SDK's configuration files (which is usually at
52# ${HOME}/.config).
53CLOUD_SDK_CONFIG_ENV = 'CLOUDSDK_CONFIG'
54
55# If set, this is where the environment setup will install the Cloud SDK.
56# If unset, it will download the SDK to a temporary directory.
57CLOUD_SDK_ROOT = os.environ.get('CLOUD_SDK_ROOT')
58
59if CLOUD_SDK_ROOT is not None:
60 CLOUD_SDK_ROOT = py.path.local(CLOUD_SDK_ROOT)
61 CLOUD_SDK_ROOT.ensure(dir=True) # Makes sure the directory exists.
62else:
63 CLOUD_SDK_ROOT = py.path.local.mkdtemp()
64
65# The full path the cloud sdk install directory
66CLOUD_SDK_INSTALL_DIR = CLOUD_SDK_ROOT.join('google-cloud-sdk')
67
68# The full path to the gcloud cli executable.
69GCLOUD = str(CLOUD_SDK_INSTALL_DIR.join('bin', 'gcloud'))
70
71# gcloud requires Python 2 and doesn't work on 3, so we need to tell it
72# where to find 2 when we're running in a 3 environment.
73CLOUD_SDK_PYTHON_ENV = 'CLOUDSDK_PYTHON'
74CLOUD_SDK_PYTHON = which('python2', None)
75
76# Cloud SDK helpers
77
78
79def install_cloud_sdk(session):
80 """Downloads and installs the Google Cloud SDK."""
81 # Configure environment variables needed by the SDK.
82 # This sets the config root to the tests' config root. This prevents
83 # our tests from clobbering a developer's configuration when running
84 # these tests locally.
85 session.env[CLOUD_SDK_CONFIG_ENV] = str(CLOUD_SDK_ROOT)
86 # This tells gcloud which Python interpreter to use (always use 2.7)
87 session.env[CLOUD_SDK_PYTHON_ENV] = CLOUD_SDK_PYTHON
Jon Wayne Parrott0c09c732017-03-24 12:10:44 -070088 # This set the $PATH for the subprocesses so they can find the gcloud
89 # executable.
90 session.env['PATH'] = (
91 str(CLOUD_SDK_INSTALL_DIR.join('bin')) + os.pathsep +
92 os.environ['PATH'])
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070093
Jon Wayne Parrott87161412017-03-01 09:28:15 -080094 # If gcloud cli executable already exists, just update it.
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070095 if py.path.local(GCLOUD).exists():
Jon Wayne Parrott87161412017-03-01 09:28:15 -080096 session.run(GCLOUD, 'components', 'update', '-q')
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070097 return
98
99 tar_path = CLOUD_SDK_ROOT.join(CLOUD_SDK_DIST_FILENAME)
100
101 # Download the release.
102 session.run(
103 'wget', CLOUD_SDK_DOWNLOAD_URL, '-O', str(tar_path), silent=True)
104
105 # Extract the release.
106 session.run(
107 'tar', 'xzf', str(tar_path), '-C', str(CLOUD_SDK_ROOT))
108 session.run(tar_path.remove)
109
110 # Run the install script.
111 session.run(
112 str(CLOUD_SDK_INSTALL_DIR.join('install.sh')),
113 '--usage-reporting', 'false',
114 '--path-update', 'false',
115 '--command-completion', 'false',
116 silent=True)
117
118
119def copy_credentials(credentials_path):
120 """Copies credentials into the SDK root as the application default
121 credentials."""
122 dest = CLOUD_SDK_ROOT.join('application_default_credentials.json')
123 if dest.exists():
124 dest.remove()
125 py.path.local(credentials_path).copy(dest)
126
127
128def configure_cloud_sdk(
129 session, application_default_credentials, project=False):
130 """Installs and configures the Cloud SDK with the given application default
131 credentials.
132
133 If project is True, then a project will be set in the active config.
134 If it is false, this will ensure no project is set.
135 """
136 install_cloud_sdk(session)
137
Jon Wayne Parrott0c09c732017-03-24 12:10:44 -0700138 # Setup the service account as the default user account. This is
139 # needed for the project ID detection to work. Note that this doesn't
140 # change the application default credentials file, which is user
141 # credentials instead of service account credentials sometimes.
142 session.run(
143 GCLOUD, 'auth', 'activate-service-account', '--key-file',
144 SERVICE_ACCOUNT_FILE)
145
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700146 if project:
147 session.run(GCLOUD, 'config', 'set', 'project', 'example-project')
148 else:
149 session.run(GCLOUD, 'config', 'unset', 'project')
150
151 # Copy the credentials file to the config root. This is needed because
152 # unfortunately gcloud doesn't provide a clean way to tell it to use
153 # a particular set of credentials. However, this does verify that gcloud
154 # also considers the credentials valid by calling application-default
155 # print-access-token
156 session.run(copy_credentials, application_default_credentials)
157
158 # Calling this forces the Cloud SDK to read the credentials we just wrote
159 # and obtain a new access token with those credentials. This validates
160 # that our credentials matches the format expected by gcloud.
161 # Silent is set to True to prevent leaking secrets in test logs.
162 session.run(
163 GCLOUD, 'auth', 'application-default', 'print-access-token',
164 silent=True)
165
166
167# Test sesssions
168
169
170def session_service_account(session):
171 session.virtualenv = False
172 session.run('pytest', 'test_service_account.py')
173
174
175def session_oauth2_credentials(session):
176 session.virtualenv = False
177 session.run('pytest', 'test_oauth2_credentials.py')
178
179
180def session_default_explicit_service_account(session):
181 session.virtualenv = False
182 session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE
183 session.env[EXPECT_PROJECT_ENV] = '1'
184 session.run('pytest', 'test_default.py')
185
186
187def session_default_explicit_authorized_user(session):
188 session.virtualenv = False
189 session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE
190 session.run('pytest', 'test_default.py')
191
192
193def session_default_explicit_authorized_user_explicit_project(session):
194 session.virtualenv = False
195 session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE
196 session.env[EXPLICIT_PROJECT_ENV] = 'example-project'
197 session.env[EXPECT_PROJECT_ENV] = '1'
198 session.run('pytest', 'test_default.py')
199
200
201def session_default_cloud_sdk_service_account(session):
202 session.virtualenv = False
203 configure_cloud_sdk(session, SERVICE_ACCOUNT_FILE)
204 session.env[EXPECT_PROJECT_ENV] = '1'
205 session.run('pytest', 'test_default.py')
206
207
208def session_default_cloud_sdk_authorized_user(session):
209 session.virtualenv = False
210 configure_cloud_sdk(session, AUTHORIZED_USER_FILE)
211 session.run('pytest', 'test_default.py')
212
213
214def session_default_cloud_sdk_authorized_user_configured_project(session):
215 session.virtualenv = False
216 configure_cloud_sdk(session, AUTHORIZED_USER_FILE, project=True)
217 session.env[EXPECT_PROJECT_ENV] = '1'
218 session.run('pytest', 'test_default.py')
219
220
221def session_compute_engine(session):
222 session.virtualenv = False
223 session.run('pytest', 'test_compute_engine.py')
224
225
226def session_app_engine(session):
227 session.virtualenv = False
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700228
229 if SKIP_GAE_TEST_ENV in os.environ:
230 session.log('Skipping App Engine tests.')
231 return
232
233 # Unlike the default tests above, the App Engine system test require a
234 # 'real' gcloud sdk installation that is configured to deploy to an
235 # app engine project.
236 # Grab the project ID from the cloud sdk.
237 project_id = subprocess.check_output([
238 'gcloud', 'config', 'list', 'project', '--format',
Jon Wayne Parrottdc884c02016-12-07 14:33:25 -0800239 'value(core.project)']).decode('utf-8').strip()
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700240
241 if not project_id:
242 session.error(
243 'The Cloud SDK must be installed and configured to deploy to App '
244 'Engine.')
245
246 application_url = GAE_APP_URL_TMPL.format(
247 GAE_TEST_APP_SERVICE, project_id)
248
249 # Vendor in the test application's dependencies
250 session.chdir(os.path.join(HERE, 'app_engine_test_app'))
251 session.run(
252 'pip', 'install', '--target', 'lib', '-r', 'requirements.txt',
253 silent=True)
254
255 # Deploy the application.
256 session.run('gcloud', 'app', 'deploy', '-q', 'app.yaml')
257
258 # Run the tests
259 session.env['TEST_APP_URL'] = application_url
260 session.chdir(HERE)
261 session.run('pytest', 'test_app_engine.py')
Jon Wayne Parrottb9897dc2016-11-02 20:31:14 -0700262
263
264def session_grpc(session):
265 session.virtualenv = False
266 session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE
267 session.run('pytest', 'test_grpc.py')