• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 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################################################################################
16"""Cloud function to request builds."""
17import base64
18
19import google.auth
20from google.cloud import ndb
21
22import build_project
23from datastore_entities import BuildsHistory
24from datastore_entities import Project
25
26BASE_PROJECT = 'oss-fuzz-base'
27MAX_BUILD_HISTORY_LENGTH = 64
28QUEUE_TTL_SECONDS = 60 * 60 * 24  # 24 hours.
29
30
31def update_build_history(project_name, build_id, build_tag):
32  """Update build history of project."""
33  project_key = ndb.Key(BuildsHistory, project_name + '-' + build_tag)
34  project = project_key.get()
35
36  if not project:
37    project = BuildsHistory(id=project_name + '-' + build_tag,
38                            build_tag=build_tag,
39                            project=project_name,
40                            build_ids=[])
41
42  if len(project.build_ids) >= MAX_BUILD_HISTORY_LENGTH:
43    project.build_ids.pop(0)
44
45  project.build_ids.append(build_id)
46  project.put()
47
48
49def get_project_data(project_name):
50  """Retrieve project metadata from datastore."""
51  query = Project.query(Project.name == project_name)
52  project = query.get()
53  if not project:
54    raise RuntimeError(
55        f'Project {project_name} not available in cloud datastore')
56
57  return project.project_yaml_contents, project.dockerfile_contents
58
59
60def get_empty_config():
61  """Returns an empty build config."""
62  return build_project.Config(False, None, None, False)
63
64
65def get_build_steps(project_name, image_project, base_images_project):
66  """Retrieve build steps."""
67  # TODO(metzman): Figure out if we need this.
68  project_yaml_contents, dockerfile_lines = get_project_data(project_name)
69  build_config = get_empty_config()
70  return build_project.get_build_steps(project_name, project_yaml_contents,
71                                       dockerfile_lines, image_project,
72                                       base_images_project, build_config)
73
74
75def run_build(oss_fuzz_project, build_steps, credentials, build_type,
76              cloud_project):
77  """Execute build on cloud build. Wrapper around build_project.py that also
78  updates the db."""
79  build_id = build_project.run_build(oss_fuzz_project, build_steps, credentials,
80                                     build_type, cloud_project)
81  update_build_history(oss_fuzz_project, build_id, build_type)
82
83
84# pylint: disable=no-member
85def request_build(event, context):
86  """Entry point for cloud function to request builds."""
87  del context  #unused
88  if 'data' in event:
89    project_name = base64.b64decode(event['data']).decode('utf-8')
90  else:
91    raise RuntimeError('Project name missing from payload')
92
93  with ndb.Client().context():
94    credentials, cloud_project = google.auth.default()
95    build_steps = get_build_steps(project_name, cloud_project, BASE_PROJECT)
96    if not build_steps:
97      return
98    run_build(
99        project_name,
100        build_steps,
101        credentials,
102        build_project.FUZZING_BUILD_TYPE,
103        cloud_project=cloud_project,
104    )
105