ftl/python/builder.py (118 lines of code) (raw):

# Copyright 2017 Google Inc. All Rights Reserved. # # 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. """This package defines the interface for orchestrating image builds.""" import json import concurrent.futures from ftl.common import builder from ftl.common import constants from ftl.common import ftl_util from ftl.common import layer_builder as base_builder from ftl.python import layer_builder as package_builder from ftl.python import python_util class Python(builder.RuntimeBase): def __init__(self, ctx, args): super(Python, self).__init__( ctx, constants.PYTHON_CACHE_NAMESPACE, args, [ constants.PIPFILE_LOCK, constants.PIPFILE, # not supported rn constants.REQUIREMENTS_TXT ]) self._virtualenv_dir = args.virtualenv_dir self._wheel_dir = ftl_util.gen_tmp_dir(constants.WHEEL_DIR) self._python_cmd = args.python_cmd.split(" ") self._pip_cmd = args.pip_cmd.split(" ") self._virtualenv_cmd = args.virtualenv_cmd.split(" ") self._venv_cmd = args.venv_cmd if self._venv_cmd: self._venv_cmd = args.venv_cmd.split(" ") self._is_phase2 = ctx.Contains(constants.PIPFILE_LOCK) def _parse_pipfile_pkgs(self): pkg_descriptor = ftl_util.descriptor_parser(self._descriptor_files, self._ctx) pipfile_json = json.loads(pkg_descriptor) pkgs = [] for pkg, info in pipfile_json['default'].iteritems(): version = info['version'] pkgs.append((pkg, version)) return pkgs def Build(self): lyr_imgs = [] lyr_imgs.append(self._base_image) interpreter_builder = package_builder.InterpreterLayerBuilder( virtualenv_dir=self._virtualenv_dir, python_cmd=self._python_cmd, virtualenv_cmd=self._virtualenv_cmd, venv_cmd=self._venv_cmd, cache_key_version=self._args.cache_key_version, cache=self._cache) interpreter_builder.BuildLayer() lyr_imgs.append(interpreter_builder.GetImage()) if ftl_util.has_pkg_descriptor(self._descriptor_files, self._ctx): # build interpreter layer if self._is_phase2: # do a phase 2 build of the package layers w/ Pipfile.lock # iterate over package/version Pipfile.lock python_util.setup_virtualenv(self._virtualenv_dir, self._virtualenv_cmd, self._python_cmd, self._venv_cmd) pkgs = self._parse_pipfile_pkgs() with ftl_util.Timing('uploading_all_package_layers'): with concurrent.futures.ThreadPoolExecutor( max_workers=constants.THREADS) as executor: future_to_params = {executor.submit( self._build_pkg, pkg, interpreter_builder, lyr_imgs): pkg for pkg in pkgs } for future in concurrent.futures.as_completed( future_to_params): future.result() else: # do a phase 1 build of the package layers w/ requirements.txt req_txt_builder = package_builder.RequirementsLayerBuilder( ctx=self._ctx, descriptor_files=self._descriptor_files, directory=self._args.directory, pkg_dir=None, wheel_dir=self._wheel_dir, virtualenv_dir=self._virtualenv_dir, python_cmd=self._python_cmd, pip_cmd=self._pip_cmd, virtualenv_cmd=self._virtualenv_cmd, venv_cmd=self._venv_cmd, dep_img_lyr=interpreter_builder, cache_key_version=self._args.cache_key_version, cache=self._cache) req_txt_builder.BuildLayer() if req_txt_builder.GetImage(): lyr_imgs.append(req_txt_builder.GetImage()) app = base_builder.AppLayerBuilder( directory=self._args.directory, destination_path=self._args.destination_path, entrypoint=self._args.entrypoint, exposed_ports=self._args.exposed_ports) app.BuildLayer() lyr_imgs.append(app.GetImage()) if self._args.additional_directory: additional_directory = base_builder.AppLayerBuilder( directory=self._args.additional_directory, destination_path=self._args.additional_directory, entrypoint=self._args.entrypoint, exposed_ports=self._args.exposed_ports) additional_directory.BuildLayer() lyr_imgs.append(additional_directory.GetImage()) ftl_image = ftl_util.AppendLayersIntoImage(lyr_imgs) self.StoreImage(ftl_image) def _build_pkg(self, pkg, interpreter_builder, lyr_imgs): pipfile_builder = package_builder.PipfileLayerBuilder( ctx=self._ctx, descriptor_files=self._descriptor_files, directory=self._args.directory, pkg_descriptor=pkg, pkg_dir=None, wheel_dir=ftl_util.gen_tmp_dir(constants.WHEEL_DIR), virtualenv_dir=self._virtualenv_dir, pip_cmd=self._pip_cmd, virtualenv_cmd=self._virtualenv_cmd, dep_img_lyr=interpreter_builder, cache_key_version=self._args.cache_key_version, cache=self._cache) pipfile_builder.BuildLayer() lyr_imgs.append(pipfile_builder.GetImage())