| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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. |
| |
| import os |
| |
| from liminal.build.image_builder import ImageBuilder |
| |
| |
| class PythonImageVersions: |
| """ |
| Handles the python versions for python images. |
| """ |
| |
| @property |
| def default_version(self): |
| return '3.7' |
| |
| @property |
| def supported_versions(self): |
| return '3.6', '3.7', '3.8', '3.9' |
| |
| def get_image_name(self, python_version): |
| """ |
| :param python_version: The python version that would be installed in |
| the docker image. For example '3.8', '3.8.1' etc. |
| :type python_version: str |
| :return: The name of the base (slim) python image |
| :rtype: str |
| """ |
| if not python_version: |
| python_version = self.default_version |
| else: |
| python_version = str(python_version) |
| if python_version[:3] not in self.supported_versions: |
| raise ValueError(f'liminal supports the following python versions: ' |
| f'{self.supported_versions} but {python_version} ' |
| f'were passed') |
| return f'python:{python_version}-slim' |
| |
| |
| class BasePythonImageBuilder(ImageBuilder): |
| """ |
| Base class for building python images. |
| """ |
| |
| __PIP_CONF = 'pip_conf' |
| __PYTHON_VERSION = 'python_version' |
| |
| def __init__(self, config, base_path, relative_source_path, tag, |
| base_image=PythonImageVersions()): |
| super().__init__(config, base_path, relative_source_path, tag) |
| self._base_image = base_image |
| |
| @staticmethod |
| def _dockerfile_path(): |
| raise NotImplementedError() |
| |
| def _write_additional_files(self, temp_dir): |
| requirements_file_path = os.path.join(temp_dir, 'requirements.txt') |
| if not os.path.exists(requirements_file_path): |
| with open(requirements_file_path, 'w'): |
| pass |
| |
| super()._write_additional_files(temp_dir) |
| |
| def _additional_files_from_filename_content_pairs(self): |
| with open(self._dockerfile_path()) as original: |
| data = original.read() |
| |
| data = self.__mount_pip_conf(data) |
| data = self.__add_python_base_version(data) |
| |
| return [('Dockerfile', data)] |
| |
| def __mount_pip_conf(self, data): |
| new_data = data |
| |
| if self.__PIP_CONF in self.config: |
| new_data = '# syntax = docker/dockerfile:1.0-experimental\n' + data |
| new_data = new_data.replace('{{mount}}', |
| '--mount=type=secret,id=pip_config,dst=/etc/pip.conf \\\n') |
| else: |
| new_data = new_data.replace('{{mount}} ', '') |
| |
| return new_data |
| |
| def __add_python_base_version(self, data): |
| python_version = self.config.get(self.__PYTHON_VERSION) |
| base_image = self._base_image.get_image_name(python_version) |
| return data.replace('{{python}}', base_image) |
| |
| def _build_flags(self): |
| if self.__PIP_CONF in self.config: |
| return f'--secret id=pip_config,src={self.config[self.__PIP_CONF]}' |
| else: |
| return '' |
| |
| def _use_buildkit(self): |
| if self.__PIP_CONF in self.config: |
| return True |