| # 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 to request builds.""" |
| import base64 |
| |
| import google.auth |
| from google.cloud import ndb |
| |
| import build_project |
| from datastore_entities import BuildsHistory |
| from datastore_entities import Project |
| |
| BASE_PROJECT = 'oss-fuzz-base' |
| MAX_BUILD_HISTORY_LENGTH = 64 |
| QUEUE_TTL_SECONDS = 60 * 60 * 24 # 24 hours. |
| |
| |
| def update_build_history(project_name, build_id, build_tag): |
| """Update build history of project.""" |
| project_key = ndb.Key(BuildsHistory, project_name + '-' + build_tag) |
| project = project_key.get() |
| |
| if not project: |
| project = BuildsHistory(id=project_name + '-' + build_tag, |
| build_tag=build_tag, |
| project=project_name, |
| build_ids=[]) |
| |
| if len(project.build_ids) >= MAX_BUILD_HISTORY_LENGTH: |
| project.build_ids.pop(0) |
| |
| project.build_ids.append(build_id) |
| project.put() |
| |
| |
| def get_project_data(project_name): |
| """Retrieve project metadata from datastore.""" |
| query = Project.query(Project.name == project_name) |
| project = query.get() |
| if not project: |
| raise RuntimeError( |
| f'Project {project_name} not available in cloud datastore') |
| |
| return project.project_yaml_contents, project.dockerfile_contents |
| |
| |
| def get_empty_config(): |
| """Returns an empty build config.""" |
| return build_project.Config(False, None, None, False) |
| |
| |
| def get_build_steps(project_name, image_project, base_images_project): |
| """Retrieve build steps.""" |
| # TODO(metzman): Figure out if we need this. |
| project_yaml_contents, dockerfile_lines = get_project_data(project_name) |
| build_config = get_empty_config() |
| return build_project.get_build_steps(project_name, project_yaml_contents, |
| dockerfile_lines, image_project, |
| base_images_project, build_config) |
| |
| |
| def run_build(oss_fuzz_project, build_steps, credentials, build_type, |
| cloud_project): |
| """Execute build on cloud build. Wrapper around build_project.py that also |
| updates the db.""" |
| build_id = build_project.run_build(oss_fuzz_project, build_steps, credentials, |
| build_type, cloud_project) |
| update_build_history(oss_fuzz_project, build_id, build_type) |
| |
| |
| # 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') |
| |
| with ndb.Client().context(): |
| credentials, cloud_project = google.auth.default() |
| build_steps = get_build_steps(project_name, cloud_project, BASE_PROJECT) |
| if not build_steps: |
| return |
| run_build( |
| project_name, |
| build_steps, |
| credentials, |
| build_project.FUZZING_BUILD_TYPE, |
| cloud_project=cloud_project, |
| ) |