aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar kabeer27 <32016558+kabeer27@users.noreply.github.com>2020-07-20 12:00:48 +0530
committerGravatar GitHub <noreply@github.com>2020-07-20 16:30:48 +1000
commit769c6e80f2f609549ada54b62e9cb3478faa7da3 (patch)
treecfd59b725dcdafb567915625397aa3be6db39ec3
parent0a334b3286c6fd44a890dccaf37fef6def110f69 (diff)
Adding build coverage cloud function, and refactoring test_utils.py (#4151)
* Adding build coverage cloud function, and refactoring test_utils.py * Multiple changes will add a comment. * Catching sys.exit() exception and removing useless wrapper build_steps function * Comment update * Comment update * Comment formatting
l---------infra/build/functions/build_and_run_coverage.py1
-rwxr-xr-xinfra/build/functions/deploy.sh33
-rw-r--r--infra/build/functions/expected_coverage_build_steps.json144
-rw-r--r--infra/build/functions/main.py12
-rw-r--r--infra/build/functions/project_sync.py (renamed from infra/build/functions/sync.py)0
-rw-r--r--infra/build/functions/project_sync_test.py (renamed from infra/build/functions/sync_test.py)17
-rw-r--r--infra/build/functions/request_build.py31
-rw-r--r--infra/build/functions/request_build_test.py25
-rw-r--r--infra/build/functions/request_coverage_build.py47
-rw-r--r--infra/build/functions/request_coverage_build_test.py84
-rw-r--r--infra/build/functions/test_utils.py20
-rw-r--r--infra/gcb/build_and_run_coverage.py1
12 files changed, 361 insertions, 54 deletions
diff --git a/infra/build/functions/build_and_run_coverage.py b/infra/build/functions/build_and_run_coverage.py
new file mode 120000
index 00000000..4fe4b575
--- /dev/null
+++ b/infra/build/functions/build_and_run_coverage.py
@@ -0,0 +1 @@
+../../gcb/build_and_run_coverage.py \ No newline at end of file
diff --git a/infra/build/functions/deploy.sh b/infra/build/functions/deploy.sh
index f7c68d96..d9ec203f 100755
--- a/infra/build/functions/deploy.sh
+++ b/infra/build/functions/deploy.sh
@@ -21,11 +21,17 @@ BASE_IMAGE_MESSAGE="Start base image build"
BUILD_JOB_TOPIC=request-build
+COVERAGE_BUILD_JOB_TOPIC=request-coverage-build
+COVERAGE_BUILD_SCHEDULER_JOB=coverage-build-scheduler
+COVERAGE_BUILD_SCHEDULE="0 6 * * *"
+COVERAGE_BUILD_MESSAGE="Start coverage report builds"
+
SYNC_JOB_TOPIC=schedule-project-sync
SYNC_SCHEDULER_JOB=sync-scheduler
SYNC_JOB_SCHEDULE="*/30 * * * *"
SYNC_MESSAGE="Start Sync"
+
function deploy_pubsub_topic {
topic=$1
project=$2
@@ -68,21 +74,23 @@ function deploy_cloud_function {
gcloud functions deploy $name \
--entry-point $entry_point \
- --trigger-topic topic \
+ --trigger-topic $topic \
--runtime python37 \
--project $project \
--timeout 540
}
-if [ "$1" ]; then
+if [ $# == 2 ]; then
PROJECT_ID=$1
+ BASE_PROJECT_ID=$2
else
- echo -e "\n Usage ./deploy.sh my-project-name"; exit;
+ echo -e "\n Usage ./deploy.sh <project-name> <base-project-name>"; exit;
fi
deploy_pubsub_topic $BUILD_JOB_TOPIC $PROJECT_ID
deploy_pubsub_topic $SYNC_JOB_TOPIC $PROJECT_ID
-deploy_pubsub_topic $BASE_IMAGE_JOB_TOPIC $PROJECT_ID
+deploy_pubsub_topic $BASE_IMAGE_JOB_TOPIC $BASE_PROJECT_ID
+deploy_pubsub_topic $COVERAGE_BUILD_JOB_TOPIC $PROJECT_ID
deploy_scheduler $SYNC_SCHEDULER_JOB \
"$SYNC_JOB_SCHEDULE" \
@@ -94,19 +102,30 @@ deploy_scheduler $BASE_IMAGE_SCHEDULER_JOB \
"$BASE_IMAGE SCHEDULE" \
$BASE_IMAGE_JOB_TOPIC \
"$BASE_IMAGE_MESSAGE" \
- $PROJECT_ID
+ $BASE_PROJECT_ID
+
+deploy_scheduler $COVERAGE_BUILD_SCHEDULER_JOB \
+ "$COVERAGE_BUILD_SCHEDULE" \
+ $COVERAGE_BUILD_JOB_TOPIC \
+ "$COVERAGE_BUILD_MESSAGE" \
+ $PROJECT_ID
deploy_cloud_function sync \
- project_sync \
+ sync \
$SYNC_JOB_TOPIC \
$PROJECT_ID
deploy_cloud_function base-image-build \
build_base_images \
$BASE_IMAGE_JOB_TOPIC \
- $PROJECT_ID
+ $BASE_PROJECT_ID
deploy_cloud_function request-build \
build_project \
$BUILD_JOB_TOPIC \
$PROJECT_ID
+
+deploy_cloud_function request-coverage-build \
+ coverage_build \
+ $COVERAGE_BUILD_JOB_TOPIC \
+ $PROJECT_ID
diff --git a/infra/build/functions/expected_coverage_build_steps.json b/infra/build/functions/expected_coverage_build_steps.json
new file mode 100644
index 00000000..19b1d5b8
--- /dev/null
+++ b/infra/build/functions/expected_coverage_build_steps.json
@@ -0,0 +1,144 @@
+[
+ {
+ "args": [
+ "clone",
+ "https://github.com/google/oss-fuzz.git"
+ ],
+ "name": "gcr.io/cloud-builders/git"
+ },
+ {
+ "name": "gcr.io/cloud-builders/docker",
+ "args": [
+ "build",
+ "-t",
+ "gcr.io/oss-fuzz/test-project",
+ "."
+ ],
+ "dir": "oss-fuzz/projects/test-project"
+ },
+ {
+ "name": "gcr.io/oss-fuzz/test-project",
+ "args": [
+ "bash",
+ "-c",
+ "srcmap > /workspace/srcmap.json && cat /workspace/srcmap.json"
+ ],
+ "env": [
+ "OSSFUZZ_REVISION=$REVISION_ID",
+ "FUZZING_LANGUAGE=c++"
+ ]
+ },
+ {
+ "name": "gcr.io/oss-fuzz/test-project",
+ "env": [
+ "FUZZING_ENGINE=libfuzzer",
+ "SANITIZER=coverage",
+ "OUT=/workspace/out/coverage",
+ "FUZZING_LANGUAGE=c++"
+ ],
+ "args": [
+ "bash",
+ "-c",
+ "rm -r /out && cd /src && cd /src && mkdir -p /workspace/out/coverage && compile || (echo \"********************************************************************************\nCoverage build failed.\nTo reproduce, run:\npython infra/helper.py build_image test-project\npython infra/helper.py build_fuzzers --sanitizer coverage test-project\n********************************************************************************\" && false)"
+ ]
+ },
+ {
+ "url": "test_download"
+ },
+ {
+ "name": "gcr.io/oss-fuzz-base/base-runner",
+ "env": [
+ "FUZZING_ENGINE=libfuzzer",
+ "SANITIZER=coverage",
+ "OUT=/workspace/out/coverage",
+ "FUZZING_LANGUAGE=c++",
+ "HTTP_PORT=",
+ "COVERAGE_EXTRA_ARGS="
+ ],
+ "args": [
+ "bash",
+ "-c",
+ "for f in /corpus/*.zip; do unzip -q $f -d ${f%%.*} || (echo \"Failed to unpack the corpus for $(basename ${f%%.*}). This usually means that corpus backup for a particular fuzz target does not exist. If a fuzz target was added in the last 24 hours, please wait one more day. Otherwise, something is wrong with the fuzz target or the infrastructure, and corpus pruning task does not finish successfully.\" && exit 1); done && coverage || (echo \"********************************************************************************\nCode coverage report generation failed.\nTo reproduce, run:\npython infra/helper.py build_image test-project\npython infra/helper.py build_fuzzers --sanitizer coverage test-project\npython infra/helper.py coverage test-project\n********************************************************************************\" && false)"
+ ],
+ "volumes": [
+ {
+ "name": "corpus",
+ "path": "/corpus"
+ }
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "entrypoint": "sh",
+ "args": [
+ "-c",
+ "gsutil -m rm -rf gs://oss-fuzz-coverage/test-project/reports/20200101 || exit 0"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "args": [
+ "-m",
+ "cp",
+ "-r",
+ "/workspace/out/coverage/report",
+ "gs://oss-fuzz-coverage/test-project/reports/20200101"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "entrypoint": "sh",
+ "args": [
+ "-c",
+ "gsutil -m rm -rf gs://oss-fuzz-coverage/test-project/fuzzer_stats/20200101 || exit 0"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "args": [
+ "-m",
+ "cp",
+ "-r",
+ "/workspace/out/coverage/fuzzer_stats",
+ "gs://oss-fuzz-coverage/test-project/fuzzer_stats/20200101"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "entrypoint": "sh",
+ "args": [
+ "-c",
+ "gsutil -m rm -rf gs://oss-fuzz-coverage/test-project/logs/20200101 || exit 0"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "args": [
+ "-m",
+ "cp",
+ "-r",
+ "/workspace/out/coverage/logs",
+ "gs://oss-fuzz-coverage/test-project/logs/20200101"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/gsutil",
+ "args": [
+ "cp",
+ "/workspace/srcmap.json",
+ "gs://oss-fuzz-coverage/test-project/srcmap/20200101.json"
+ ]
+ },
+ {
+ "name": "gcr.io/cloud-builders/curl",
+ "args": [
+ "-H",
+ "Content-Type: application/json",
+ "-X",
+ "PUT",
+ "-d",
+ "{\"fuzzer_stats_dir\": \"gs://oss-fuzz-coverage/test-project/fuzzer_stats/20200101\", \"html_report_url\": \"https://storage.googleapis.com/oss-fuzz-coverage/test-project/reports/20200101/linux/index.html\", \"report_date\": \"20200101\", \"report_summary_path\": \"gs://oss-fuzz-coverage/test-project/reports/20200101/linux/summary.json\"}",
+ "test_url"
+ ]
+ }
+] \ No newline at end of file
diff --git a/infra/build/functions/main.py b/infra/build/functions/main.py
index 04fb024a..dace1f8b 100644
--- a/infra/build/functions/main.py
+++ b/infra/build/functions/main.py
@@ -16,8 +16,9 @@
"""Cloud functions for build infrastructure."""
import base_images
-import sync
+import project_sync
import request_build
+import request_coverage_build
def build_project(event, context):
@@ -25,11 +26,16 @@ def build_project(event, context):
request_build.request_build(event, context)
-def project_sync(event, context):
+def sync(event, context):
"""Entry point for cloud function that syncs projects from github."""
- sync.sync(event, context)
+ project_sync.sync(event, context)
def build_base_images(event, context):
"""Entry point for cloud function that builds base images."""
base_images.base_builder(event, context)
+
+
+def coverage_build(event, context):
+ """Entry point for cloud function to build coverage reports."""
+ request_coverage_build.request_coverage_build(event, context)
diff --git a/infra/build/functions/sync.py b/infra/build/functions/project_sync.py
index 8ceabe68..8ceabe68 100644
--- a/infra/build/functions/sync.py
+++ b/infra/build/functions/project_sync.py
diff --git a/infra/build/functions/sync_test.py b/infra/build/functions/project_sync_test.py
index 203eab6c..34fd674c 100644
--- a/infra/build/functions/sync_test.py
+++ b/infra/build/functions/project_sync_test.py
@@ -16,16 +16,15 @@
"""Unit tests for Cloud Function sync, which syncs the list of github projects
and uploads them to the Cloud Datastore."""
-import os
import unittest
from google.cloud import ndb
from datastore_entities import Project
-from sync import get_access_token
-from sync import get_projects
-from sync import ProjectMetadata
-from sync import sync_projects
+from project_sync import get_access_token
+from project_sync import get_projects
+from project_sync import ProjectMetadata
+from project_sync import sync_projects
import test_utils
@@ -101,12 +100,7 @@ class TestDataSync(unittest.TestCase):
cls.ds_emulator = test_utils.start_datastore_emulator()
test_utils.wait_for_emulator_ready(cls.ds_emulator, 'datastore',
test_utils.DATASTORE_READY_INDICATOR)
- os.environ['DATASTORE_EMULATOR_HOST'] = 'localhost:' + str(
- test_utils.DATASTORE_EMULATOR_PORT)
- os.environ['GOOGLE_CLOUD_PROJECT'] = test_utils.TEST_PROJECT_ID
- os.environ['DATASTORE_DATASET'] = test_utils.TEST_PROJECT_ID
- os.environ['GCP_PROJECT'] = 'test-project'
- os.environ['FUNCTION_REGION'] = 'us-central1'
+ test_utils.set_gcp_environment()
def setUp(self):
test_utils.reset_ds_emulator()
@@ -289,7 +283,6 @@ class TestDataSync(unittest.TestCase):
@classmethod
def tearDownClass(cls):
- # TODO: replace this with a cleaner way of killing the process
test_utils.cleanup_emulator(cls.ds_emulator)
diff --git a/infra/build/functions/request_build.py b/infra/build/functions/request_build.py
index 2a76d69e..44b2fcf1 100644
--- a/infra/build/functions/request_build.py
+++ b/infra/build/functions/request_build.py
@@ -16,7 +16,6 @@
"""Cloud function to request builds."""
import base64
import logging
-import sys
import google.auth
from googleapiclient.discovery import build
@@ -54,25 +53,15 @@ def get_build_steps(project_name, image_project, base_images_project):
# pylint: disable=no-member
-def request_build(event, context):
- """Entry point for cloud function to request builds."""
- del context #unused
- if 'data' in event:
- project_name = base64.b64decode(event['data']).decode('utf-8')
- else:
- logging.error('Project name missing from payload')
- sys.exit(1)
-
- credentials, image_project = google.auth.default()
- build_steps = get_build_steps(project_name, image_project, BASE_PROJECT)
-
+def run_build(project_name, image_project, build_steps, credentials, tag):
+ """Execute build on cloud build."""
build_body = {
'steps': build_steps,
'timeout': str(build_lib.BUILD_TIMEOUT) + 's',
'options': {
'machineType': 'N1_HIGHCPU_32'
},
- 'tags': [project_name + '-fuzzing',],
+ 'tags': [project_name + tag,],
}
cloudbuild = build('cloudbuild',
@@ -85,3 +74,17 @@ def request_build(event, context):
logging.info('Build ID: %s', build_id)
logging.info('Logs: %s', build_project.get_logs_url(build_id, image_project))
+
+
+# pylint: disable=no-member
+def request_build(event, context):
+ """Entry point for cloud function to request builds."""
+ del context #unused
+ if 'data' in event:
+ project_name = base64.b64decode(event['data']).decode('utf-8')
+ else:
+ raise RuntimeError('Project name missing from payload')
+
+ credentials, image_project = google.auth.default()
+ build_steps = get_build_steps(project_name, image_project, BASE_PROJECT)
+ run_build(project_name, image_project, build_steps, credentials, '-fuzzing')
diff --git a/infra/build/functions/request_build_test.py b/infra/build/functions/request_build_test.py
index a436a30a..2af82eb5 100644
--- a/infra/build/functions/request_build_test.py
+++ b/infra/build/functions/request_build_test.py
@@ -27,15 +27,6 @@ from request_build import get_build_steps
import test_utils
-# pylint: disable=arguments-differ
-class SpoofedDatetime(datetime.datetime):
- """Mocking Datetime class for now() function."""
-
- @classmethod
- def now(cls):
- return datetime.datetime(2020, 1, 1, 0, 0, 0)
-
-
class TestRequestBuilds(unittest.TestCase):
"""Unit tests for sync."""
@@ -44,12 +35,7 @@ class TestRequestBuilds(unittest.TestCase):
cls.ds_emulator = test_utils.start_datastore_emulator()
test_utils.wait_for_emulator_ready(cls.ds_emulator, 'datastore',
test_utils.DATASTORE_READY_INDICATOR)
- os.environ['DATASTORE_EMULATOR_HOST'] = 'localhost:' + str(
- test_utils.DATASTORE_EMULATOR_PORT)
- os.environ['GOOGLE_CLOUD_PROJECT'] = test_utils.TEST_PROJECT_ID
- os.environ['DATASTORE_DATASET'] = test_utils.TEST_PROJECT_ID
- os.environ['GCP_PROJECT'] = 'test-project'
- os.environ['FUNCTION_REGION'] = 'us-central1'
+ test_utils.set_gcp_environment()
def setUp(self):
test_utils.reset_ds_emulator()
@@ -59,8 +45,12 @@ class TestRequestBuilds(unittest.TestCase):
def test_get_build_steps(self, mocked_url, mocked_time):
"""Test for get_build_steps."""
del mocked_url, mocked_time
- datetime.datetime = SpoofedDatetime
- project_yaml_contents = 'language: c++\nsanitizers:\n - address\narchitectures:\n - x86_64\n'
+ datetime.datetime = test_utils.SpoofedDatetime
+ project_yaml_contents = ('language: c++\n'
+ 'sanitizers:\n'
+ ' - address\n'
+ 'architectures:\n'
+ ' - x86_64\n')
image_project = 'oss-fuzz'
base_images_project = 'oss-fuzz-base'
testcase_path = os.path.join(os.path.dirname(__file__),
@@ -85,7 +75,6 @@ class TestRequestBuilds(unittest.TestCase):
@classmethod
def tearDownClass(cls):
- # TODO: replace this with a cleaner way of killing the process
test_utils.cleanup_emulator(cls.ds_emulator)
diff --git a/infra/build/functions/request_coverage_build.py b/infra/build/functions/request_coverage_build.py
new file mode 100644
index 00000000..d874f0ea
--- /dev/null
+++ b/infra/build/functions/request_coverage_build.py
@@ -0,0 +1,47 @@
+# Copyright 2020 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+"""Cloud function that requests coverage builds."""
+
+import google.auth
+from google.cloud import ndb
+
+import build_and_run_coverage
+from datastore_entities import Project
+import request_build
+
+BASE_PROJECT = 'oss-fuzz-base'
+
+
+def request_coverage_build(event, context):
+ """Entry point for coverage build cloud function."""
+ del event, context #unused
+
+ with ndb.Client().context():
+ credentials, image_project = google.auth.default()
+ for project in Project.query():
+ project_name = project.name
+ project_yaml_contents = project.project_yaml_contents
+ dockerfile_lines = project.dockerfile_contents.split('\n')
+ # Catching sys.exit() for a project's build steps to avoid it
+ # from interferring with other remaining builds.
+ try:
+ build_steps = build_and_run_coverage.get_build_steps(
+ project_name, project_yaml_contents, dockerfile_lines,
+ image_project, BASE_PROJECT)
+ except SystemExit:
+ continue
+ request_build.run_build(project_name, image_project, build_steps,
+ credentials, '-coverage')
diff --git a/infra/build/functions/request_coverage_build_test.py b/infra/build/functions/request_coverage_build_test.py
new file mode 100644
index 00000000..0264e80e
--- /dev/null
+++ b/infra/build/functions/request_coverage_build_test.py
@@ -0,0 +1,84 @@
+# Copyright 2020 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+"""Unit tests for Cloud Function that builds coverage reports."""
+import json
+import datetime
+import os
+import unittest
+from unittest import mock
+
+from google.cloud import ndb
+
+from datastore_entities import Project
+from build_and_run_coverage import get_build_steps
+import test_utils
+
+
+class TestRequestCoverageBuilds(unittest.TestCase):
+ """Unit tests for sync."""
+
+ @classmethod
+ def setUpClass(cls):
+ cls.ds_emulator = test_utils.start_datastore_emulator()
+ test_utils.wait_for_emulator_ready(cls.ds_emulator, 'datastore',
+ test_utils.DATASTORE_READY_INDICATOR)
+ test_utils.set_gcp_environment()
+
+ def setUp(self):
+ test_utils.reset_ds_emulator()
+
+ @mock.patch('build_lib.get_signed_url', return_value='test_url')
+ @mock.patch('build_lib.download_corpora_steps',
+ return_value=[{
+ 'url': 'test_download'
+ }])
+ @mock.patch('datetime.datetime')
+ def test_get_coverage_build_steps(self, mocked_url, mocked_corpora_steps,
+ mocked_time):
+ """Test for get_build_steps."""
+ del mocked_url, mocked_corpora_steps, mocked_time
+ datetime.datetime = test_utils.SpoofedDatetime
+ project_yaml_contents = ('language: c++\n'
+ 'sanitizers:\n'
+ ' - address\n'
+ 'architectures:\n'
+ ' - x86_64\n')
+ dockerfile_contents = 'test line'
+ image_project = 'oss-fuzz'
+ base_images_project = 'oss-fuzz-base'
+ testcase_path = os.path.join(os.path.dirname(__file__),
+ 'expected_coverage_build_steps.json')
+ with open(testcase_path) as testcase_file:
+ expected_coverage_build_steps = json.load(testcase_file)
+
+ with ndb.Client().context():
+ Project(name='test-project',
+ project_yaml_contents=project_yaml_contents,
+ dockerfile_contents=dockerfile_contents).put()
+
+ dockerfile_lines = dockerfile_contents.split('\n')
+ build_steps = get_build_steps('test-project', project_yaml_contents,
+ dockerfile_lines, image_project,
+ base_images_project)
+ self.assertEqual(build_steps, expected_coverage_build_steps)
+
+ @classmethod
+ def tearDownClass(cls):
+ test_utils.cleanup_emulator(cls.ds_emulator)
+
+
+if __name__ == '__main__':
+ unittest.main(exit=False)
diff --git a/infra/build/functions/test_utils.py b/infra/build/functions/test_utils.py
index d6859a78..9aac8eac 100644
--- a/infra/build/functions/test_utils.py
+++ b/infra/build/functions/test_utils.py
@@ -14,6 +14,7 @@
#
################################################################################
"""Utility functions for testing cloud functions."""
+import datetime
import os
import subprocess
import threading
@@ -26,6 +27,15 @@ EMULATOR_TIMEOUT = 20
TEST_PROJECT_ID = 'test-project'
+# pylint: disable=arguments-differ
+class SpoofedDatetime(datetime.datetime):
+ """Mocking Datetime class for now() function."""
+
+ @classmethod
+ def now(cls):
+ return datetime.datetime(2020, 1, 1, 0, 0, 0)
+
+
def start_datastore_emulator():
"""Start Datastore emulator."""
return subprocess.Popen([
@@ -82,3 +92,13 @@ def cleanup_emulator(ds_emulator):
"""Cleanup the system processes made by ds emulator."""
del ds_emulator #To do, find a better way to cleanup emulator
os.system('pkill -f datastore')
+
+
+def set_gcp_environment():
+ """Set environment variables for simulating in google cloud platform."""
+ os.environ['DATASTORE_EMULATOR_HOST'] = 'localhost:' + str(
+ DATASTORE_EMULATOR_PORT)
+ os.environ['GOOGLE_CLOUD_PROJECT'] = TEST_PROJECT_ID
+ os.environ['DATASTORE_DATASET'] = TEST_PROJECT_ID
+ os.environ['GCP_PROJECT'] = TEST_PROJECT_ID
+ os.environ['FUNCTION_REGION'] = 'us-central1'
diff --git a/infra/gcb/build_and_run_coverage.py b/infra/gcb/build_and_run_coverage.py
index fc4177f0..d7ef5f98 100644
--- a/infra/gcb/build_and_run_coverage.py
+++ b/infra/gcb/build_and_run_coverage.py
@@ -57,6 +57,7 @@ def skip_build(message):
# Since the script should print build_id, print '0' as a special value.
print('0')
+ # TODO: remove sys.exit call after infra migration.
sys.exit(0)