blob: 5f9291a2f6d7e9282ac38620ef68d6eea4d13160 [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__))
Bu Sun Kim9eec0912019-10-21 17:04:21 -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"
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070039
Bu Sun Kim9eec0912019-10-21 17:04:21 -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"
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -070043
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070044# The download location for the Cloud SDK
Bu Sun Kim9eec0912019-10-21 17:04:21 -070045CLOUD_SDK_DIST_FILENAME = "google-cloud-sdk.tar.gz"
46CLOUD_SDK_DOWNLOAD_URL = "https://dl.google.com/dl/cloudsdk/release/{}".format(
47 CLOUD_SDK_DIST_FILENAME
48)
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070049
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).
Bu Sun Kim9eec0912019-10-21 17:04:21 -070053CLOUD_SDK_CONFIG_ENV = "CLOUDSDK_CONFIG"
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070054
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.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070057CLOUD_SDK_ROOT = os.environ.get("CLOUD_SDK_ROOT")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070058
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
Bu Sun Kim9eec0912019-10-21 17:04:21 -070066CLOUD_SDK_INSTALL_DIR = CLOUD_SDK_ROOT.join("google-cloud-sdk")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070067
68# The full path to the gcloud cli executable.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070069GCLOUD = str(CLOUD_SDK_INSTALL_DIR.join("bin", "gcloud"))
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070070
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.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070073CLOUD_SDK_PYTHON_ENV = "CLOUDSDK_PYTHON"
74CLOUD_SDK_PYTHON = which("python2", None)
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -070075
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.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070090 session.env["PATH"] = (
91 str(CLOUD_SDK_INSTALL_DIR.join("bin")) + os.pathsep + os.environ["PATH"]
92 )
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():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070096 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.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700102 session.run("wget", CLOUD_SDK_DOWNLOAD_URL, "-O", str(tar_path), silent=True)
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700103
104 # Extract the release.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700105 session.run("tar", "xzf", str(tar_path), "-C", str(CLOUD_SDK_ROOT))
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700106 session.run(tar_path.remove)
107
108 # Run the install script.
109 session.run(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700110 str(CLOUD_SDK_INSTALL_DIR.join("install.sh")),
111 "--usage-reporting",
112 "false",
113 "--path-update",
114 "false",
115 "--command-completion",
116 "false",
117 silent=True,
118 )
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700119
120
121def copy_credentials(credentials_path):
122 """Copies credentials into the SDK root as the application default
123 credentials."""
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700124 dest = CLOUD_SDK_ROOT.join("application_default_credentials.json")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700125 if dest.exists():
126 dest.remove()
127 py.path.local(credentials_path).copy(dest)
128
129
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700130def configure_cloud_sdk(session, application_default_credentials, project=False):
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700131 """Installs and configures the Cloud SDK with the given application default
132 credentials.
133
134 If project is True, then a project will be set in the active config.
135 If it is false, this will ensure no project is set.
136 """
137 install_cloud_sdk(session)
138
Jon Wayne Parrott0c09c732017-03-24 12:10:44 -0700139 # Setup the service account as the default user account. This is
140 # needed for the project ID detection to work. Note that this doesn't
141 # change the application default credentials file, which is user
142 # credentials instead of service account credentials sometimes.
143 session.run(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700144 GCLOUD, "auth", "activate-service-account", "--key-file", SERVICE_ACCOUNT_FILE
145 )
Jon Wayne Parrott0c09c732017-03-24 12:10:44 -0700146
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700147 if project:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700148 session.run(GCLOUD, "config", "set", "project", "example-project")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700149 else:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700150 session.run(GCLOUD, "config", "unset", "project")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700151
152 # Copy the credentials file to the config root. This is needed because
153 # unfortunately gcloud doesn't provide a clean way to tell it to use
154 # a particular set of credentials. However, this does verify that gcloud
155 # also considers the credentials valid by calling application-default
156 # print-access-token
157 session.run(copy_credentials, application_default_credentials)
158
159 # Calling this forces the Cloud SDK to read the credentials we just wrote
160 # and obtain a new access token with those credentials. This validates
161 # that our credentials matches the format expected by gcloud.
162 # Silent is set to True to prevent leaking secrets in test logs.
163 session.run(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700164 GCLOUD, "auth", "application-default", "print-access-token", silent=True
165 )
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700166
167
168# Test sesssions
169
170
171def session_service_account(session):
172 session.virtualenv = False
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700173 session.run("pytest", "test_service_account.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700174
175
176def session_oauth2_credentials(session):
177 session.virtualenv = False
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700178 session.run("pytest", "test_oauth2_credentials.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700179
180
181def session_default_explicit_service_account(session):
182 session.virtualenv = False
183 session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700184 session.env[EXPECT_PROJECT_ENV] = "1"
185 session.run("pytest", "test_default.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700186
187
188def session_default_explicit_authorized_user(session):
189 session.virtualenv = False
190 session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700191 session.run("pytest", "test_default.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700192
193
194def session_default_explicit_authorized_user_explicit_project(session):
195 session.virtualenv = False
196 session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700197 session.env[EXPLICIT_PROJECT_ENV] = "example-project"
198 session.env[EXPECT_PROJECT_ENV] = "1"
199 session.run("pytest", "test_default.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700200
201
202def session_default_cloud_sdk_service_account(session):
203 session.virtualenv = False
204 configure_cloud_sdk(session, SERVICE_ACCOUNT_FILE)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700205 session.env[EXPECT_PROJECT_ENV] = "1"
206 session.run("pytest", "test_default.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700207
208
209def session_default_cloud_sdk_authorized_user(session):
210 session.virtualenv = False
211 configure_cloud_sdk(session, AUTHORIZED_USER_FILE)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700212 session.run("pytest", "test_default.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700213
214
215def session_default_cloud_sdk_authorized_user_configured_project(session):
216 session.virtualenv = False
217 configure_cloud_sdk(session, AUTHORIZED_USER_FILE, project=True)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700218 session.env[EXPECT_PROJECT_ENV] = "1"
219 session.run("pytest", "test_default.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700220
221
222def session_compute_engine(session):
223 session.virtualenv = False
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700224 session.run("pytest", "test_compute_engine.py")
Jon Wayne Parrottbbc39432016-10-27 23:12:39 -0700225
226
227def session_app_engine(session):
228 session.virtualenv = False
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700229
230 if SKIP_GAE_TEST_ENV in os.environ:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700231 session.log("Skipping App Engine tests.")
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700232 return
233
234 # Unlike the default tests above, the App Engine system test require a
235 # 'real' gcloud sdk installation that is configured to deploy to an
236 # app engine project.
237 # Grab the project ID from the cloud sdk.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700238 project_id = (
239 subprocess.check_output(
240 ["gcloud", "config", "list", "project", "--format", "value(core.project)"]
241 )
242 .decode("utf-8")
243 .strip()
244 )
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700245
246 if not project_id:
247 session.error(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700248 "The Cloud SDK must be installed and configured to deploy to App " "Engine."
249 )
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700250
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700251 application_url = GAE_APP_URL_TMPL.format(GAE_TEST_APP_SERVICE, project_id)
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700252
253 # Vendor in the test application's dependencies
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700254 session.chdir(os.path.join(HERE, "app_engine_test_app"))
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700255 session.run(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700256 "pip", "install", "--target", "lib", "-r", "requirements.txt", silent=True
257 )
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700258
259 # Deploy the application.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700260 session.run("gcloud", "app", "deploy", "-q", "app.yaml")
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700261
262 # Run the tests
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700263 session.env["TEST_APP_URL"] = application_url
Jon Wayne Parrottb551ef22016-10-28 12:35:15 -0700264 session.chdir(HERE)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700265 session.run("pytest", "test_app_engine.py")
Jon Wayne Parrottb9897dc2016-11-02 20:31:14 -0700266
267
268def session_grpc(session):
269 session.virtualenv = False
270 session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700271 session.run("pytest", "test_grpc.py")