blob: 0d1116d66de3afefb6f9da8478eb5445a55ba75e [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
88
Jon Wayne Parrott87161412017-03-01 09:28:15 -080089 # If gcloud cli executable already exists, just update it.
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070090 if py.path.local(GCLOUD).exists():
Jon Wayne Parrott87161412017-03-01 09:28:15 -080091 session.run(GCLOUD, 'components', 'update', '-q')
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070092 return
93
94 tar_path = CLOUD_SDK_ROOT.join(CLOUD_SDK_DIST_FILENAME)
95
96 # Download the release.
97 session.run(
98 'wget', CLOUD_SDK_DOWNLOAD_URL, '-O', str(tar_path), silent=True)
99
100 # Extract the release.
101 session.run(
102 'tar', 'xzf', str(tar_path), '-C', str(CLOUD_SDK_ROOT))
103 session.run(tar_path.remove)
104
105 # Run the install script.
106 session.run(
107 str(CLOUD_SDK_INSTALL_DIR.join('install.sh')),
108 '--usage-reporting', 'false',
109 '--path-update', 'false',
110 '--command-completion', 'false',
111 silent=True)
112
113
114def copy_credentials(credentials_path):
115 """Copies credentials into the SDK root as the application default
116 credentials."""
117 dest = CLOUD_SDK_ROOT.join('application_default_credentials.json')
118 if dest.exists():
119 dest.remove()
120 py.path.local(credentials_path).copy(dest)
121
122
123def configure_cloud_sdk(
124 session, application_default_credentials, project=False):
125 """Installs and configures the Cloud SDK with the given application default
126 credentials.
127
128 If project is True, then a project will be set in the active config.
129 If it is false, this will ensure no project is set.
130 """
131 install_cloud_sdk(session)
132
133 if project:
134 session.run(GCLOUD, 'config', 'set', 'project', 'example-project')
135 else:
136 session.run(GCLOUD, 'config', 'unset', 'project')
137
138 # Copy the credentials file to the config root. This is needed because
139 # unfortunately gcloud doesn't provide a clean way to tell it to use
140 # a particular set of credentials. However, this does verify that gcloud
141 # also considers the credentials valid by calling application-default
142 # print-access-token
143 session.run(copy_credentials, application_default_credentials)
144
145 # Calling this forces the Cloud SDK to read the credentials we just wrote
146 # and obtain a new access token with those credentials. This validates
147 # that our credentials matches the format expected by gcloud.
148 # Silent is set to True to prevent leaking secrets in test logs.
149 session.run(
150 GCLOUD, 'auth', 'application-default', 'print-access-token',
151 silent=True)
152
153
154# Test sesssions
155
156
157def session_service_account(session):
158 session.virtualenv = False
159 session.run('pytest', 'test_service_account.py')
160
161
162def session_oauth2_credentials(session):
163 session.virtualenv = False
164 session.run('pytest', 'test_oauth2_credentials.py')
165
166
167def session_default_explicit_service_account(session):
168 session.virtualenv = False
169 session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE
170 session.env[EXPECT_PROJECT_ENV] = '1'
171 session.run('pytest', 'test_default.py')
172
173
174def session_default_explicit_authorized_user(session):
175 session.virtualenv = False
176 session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE
177 session.run('pytest', 'test_default.py')
178
179
180def session_default_explicit_authorized_user_explicit_project(session):
181 session.virtualenv = False
182 session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE
183 session.env[EXPLICIT_PROJECT_ENV] = 'example-project'
184 session.env[EXPECT_PROJECT_ENV] = '1'
185 session.run('pytest', 'test_default.py')
186
187
188def session_default_cloud_sdk_service_account(session):
189 session.virtualenv = False
190 configure_cloud_sdk(session, SERVICE_ACCOUNT_FILE)
191 session.env[EXPECT_PROJECT_ENV] = '1'
192 session.run('pytest', 'test_default.py')
193
194
195def session_default_cloud_sdk_authorized_user(session):
196 session.virtualenv = False
197 configure_cloud_sdk(session, AUTHORIZED_USER_FILE)
198 session.run('pytest', 'test_default.py')
199
200
201def session_default_cloud_sdk_authorized_user_configured_project(session):
202 session.virtualenv = False
203 configure_cloud_sdk(session, AUTHORIZED_USER_FILE, project=True)
204 session.env[EXPECT_PROJECT_ENV] = '1'
205 session.run('pytest', 'test_default.py')
206
207
208def session_compute_engine(session):
209 session.virtualenv = False
210 session.run('pytest', 'test_compute_engine.py')
211
212
213def session_app_engine(session):
214 session.virtualenv = False
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700215
216 if SKIP_GAE_TEST_ENV in os.environ:
217 session.log('Skipping App Engine tests.')
218 return
219
220 # Unlike the default tests above, the App Engine system test require a
221 # 'real' gcloud sdk installation that is configured to deploy to an
222 # app engine project.
223 # Grab the project ID from the cloud sdk.
224 project_id = subprocess.check_output([
225 'gcloud', 'config', 'list', 'project', '--format',
Jon Wayne Parrottdc884c02016-12-07 14:33:25 -0800226 'value(core.project)']).decode('utf-8').strip()
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700227
228 if not project_id:
229 session.error(
230 'The Cloud SDK must be installed and configured to deploy to App '
231 'Engine.')
232
233 application_url = GAE_APP_URL_TMPL.format(
234 GAE_TEST_APP_SERVICE, project_id)
235
236 # Vendor in the test application's dependencies
237 session.chdir(os.path.join(HERE, 'app_engine_test_app'))
238 session.run(
239 'pip', 'install', '--target', 'lib', '-r', 'requirements.txt',
240 silent=True)
241
242 # Deploy the application.
243 session.run('gcloud', 'app', 'deploy', '-q', 'app.yaml')
244
245 # Run the tests
246 session.env['TEST_APP_URL'] = application_url
247 session.chdir(HERE)
248 session.run('pytest', 'test_app_engine.py')
Jon Wayne Parrottb9897dc2016-11-02 20:31:14 -0700249
250
251def session_grpc(session):
252 session.virtualenv = False
253 session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE
254 session.run('pytest', 'test_grpc.py')