From 0e1cd6ee858ce7420b2699d4bbaaa43d2e1955db Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Fri, 18 Jan 2019 19:59:46 +0000 Subject: [PATCH] Add python-builder docker image This is a builder image intended to make it easy to build images for projects that use pbr and bindep. Also, build a python-base image for use with python-builder pbrx installed dumb-init in a base image and used it as an entrypoint for the image. This is a good pattern that we should continue. Make a python-base image that can be used in the FROM line of consuming final images. Change-Id: I8da39e680e9c5ca0d8f603a2645e83fc3be87a8b --- .zuul.yaml | 32 +++++++++ docker/python-builder/Dockerfile | 39 ++++++++++ docker/python-builder/python3-dev.control | 17 +++++ docker/python-builder/scripts/assemble | 72 +++++++++++++++++++ .../scripts/get-extras-packages | 46 ++++++++++++ .../scripts/install-from-bindep | 25 +++++++ 6 files changed, 231 insertions(+) create mode 100644 docker/python-builder/Dockerfile create mode 100644 docker/python-builder/python3-dev.control create mode 100755 docker/python-builder/scripts/assemble create mode 100755 docker/python-builder/scripts/get-extras-packages create mode 100755 docker/python-builder/scripts/install-from-bindep diff --git a/.zuul.yaml b/.zuul.yaml index 11ac0fdf71..86bf2aa139 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -226,6 +226,35 @@ vars: *gerrit_vars files: *gerrit_files +# python-builder jobs +- job: + name: system-config-build-image-python-builder + description: Build a python-builder image. + parent: system-config-build-image + vars: &python-builder_vars + docker_images: + - context: docker/python-builder + target: python-builder + repository: opendevorg/python-builder + - context: docker/python-builder + target: python-base + repository: opendevorg/python-base + files: &python-builder_files + - docker/python-builder/.* + +- job: + name: system-config-upload-image-python-builder + description: Build and upload a python-builder image. + parent: system-config-upload-image + vars: *python-builder_vars + files: *python-builder_files + +- job: + name: system-config-promote-image-python-builder + description: Promote a previously published python-builder image to latest. + parent: system-config-promote-image + vars: *python-builder_vars + files: *python-builder_files # Role integration jobs. These test the top-level generic roles/* # under Zuul. The range of platforms should be the same as those for # openstack-zuul-jobs. @@ -512,6 +541,7 @@ - system-config-build-image-gitea-init - system-config-build-image-gitea - system-config-build-image-gerrit + - system-config-build-image-python-builder gate: jobs: - tox-linters @@ -529,9 +559,11 @@ - system-config-upload-image-gitea-init - system-config-upload-image-gitea - system-config-upload-image-gerrit + - system-config-upload-image-python-builder promote: jobs: - system-config-promote-image-jinja-init - system-config-promote-image-gitea-init - system-config-promote-image-gitea - system-config-promote-image-gerrit + - system-config-promote-image-python-builder diff --git a/docker/python-builder/Dockerfile b/docker/python-builder/Dockerfile new file mode 100644 index 0000000000..ee5da079e4 --- /dev/null +++ b/docker/python-builder/Dockerfile @@ -0,0 +1,39 @@ +# Copyright (c) 2019 Red Hat, 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. + +FROM python:slim as fake-python + +COPY python3-dev.control /tmp/python3-dev.control +WORKDIR /tmp +RUN apt-get update && apt-get install -y equivs +RUN equivs-build /tmp/python3-dev.control + +FROM python:slim as python-builder + +COPY --from=fake-python /tmp/python3-dev_4.0.0_all.deb /tmp/python3-dev_4.0.0_all.deb +COPY scripts/assemble /usr/local/bin/assemble +COPY scripts/get-extras-packages /usr/local/bin/get-extras-packages +COPY scripts/install-from-bindep /output/install-from-bindep +RUN dpkg -i /tmp/python3-dev_4.0.0_all.deb \ + && rm /tmp/python3-dev_4.0.0_all.deb \ + && pip install bindep + +FROM python:slim as python-base + +RUN apt-get update \ + && apt-get install -y dumb-init \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +ENTRYPOINT ["/usr/bin/dumb-init", "--"] diff --git a/docker/python-builder/python3-dev.control b/docker/python-builder/python3-dev.control new file mode 100644 index 0000000000..bdac43f583 --- /dev/null +++ b/docker/python-builder/python3-dev.control @@ -0,0 +1,17 @@ +# The python:slim base image already has python headers +# installed, but they are installed from source. This will +# be used to create a dummy package so that things that +# say they need python3-dev won't actually install an +# entire python that is unneeded. +Section: misc +Priority: optional +Standards-Version: 2.3.3 + +Package: python3-dev +# Set the version to 4.0.0 so that it'll always be a greater +# version than any version of python3-dev in the archive. +Version: 4.0.0 +Section: mail +Maintainer: Some Body +Architecture: all +Description: Dummy python3-dev package diff --git a/docker/python-builder/scripts/assemble b/docker/python-builder/scripts/assemble new file mode 100755 index 0000000000..16da554b10 --- /dev/null +++ b/docker/python-builder/scripts/assemble @@ -0,0 +1,72 @@ +#!/bin/bash +# Copyright (c) 2019 Red Hat, 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. + +# Make a list of bindep dependencies and a collection of built binary +# wheels for the repo in question as well as its python dependencies. +# Install javascript tools as well to support python that needs javascript +# at build time. +set -ex + +mkdir -p /output/bindep +mkdir -p /output/wheels + +cd /tmp/src + +apt-get update + +# Protect from the bindep builder image use of the assemble script +# to produce a wheel +if [ -f bindep.txt -o -f other-requirements.txt ] ; then + bindep -l newline > /output/bindep/run.txt || true + compile_packages=$(bindep -b compile || true) + if [ ! -z "$compile_packages" ] ; then + apt-get install -y ${compile_packages} + fi +fi + +# pbr needs git installed, else nothing will work +apt-get install -y git + +# Build a wheel so that we have an install target. +# pip install . in the container context with the mounted +# source dir gets ... exciting. +# We run sdist first to trigger code generation steps such +# as are found in zuul, since the sequencing otherwise +# happens in a way that makes wheel content copying unhappy. +# pip wheel isn't used here because it puts all of the output +# in the output dir and not the wheel cache, so it's not +# possible to tell what is the wheel for the project and +# what is the wheel cache. +python setup.py sdist bdist_wheel -d /output/wheels + +# Use a virtualenv for the next install steps in case to prevent +# things from the current environment from making us not build a +# wheel. +python -m venv /tmp/venv +/tmp/venv/bin/pip install -U pip wheel + +# Install everything so that the wheel cache is populated +# with transitive depends. +/tmp/venv/bin/pip install --cache-dir=/output/wheels /output/wheels/*whl + +# Install each of the extras so that we collect all possibly +# needed wheels in the wheel cache. get-extras-packages also +# writes out the req files into /output/$extra/requirements.txt. +for req in $(get-extras-packages) ; do + /tmp/venv/bin/pip install --cache-dir=/output/wheels "$req" +done + +rm -rf /tmp/venv diff --git a/docker/python-builder/scripts/get-extras-packages b/docker/python-builder/scripts/get-extras-packages new file mode 100755 index 0000000000..f3649f4fbd --- /dev/null +++ b/docker/python-builder/scripts/get-extras-packages @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 Red Hat, 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. + +import configparser +import os +import sys + + +def get_extras_packages(path): + config = configparser.ConfigParser() + config.read('setup.cfg') + config = dict(dict(config.items()).get('extras', {}).items()) + + extras = set() + for name, packages in config.items(): + for package in packages.split('\n'): + package = package.strip() + if not package: + continue + extras.add(package) + outpath = os.path.join(path, name, 'requirements.txt') + os.mkdir(os.path.dirname(outpath)) + with open(outpath, 'w') as outfile: + outfile.write(packages) + + return " ".join(extras) + + +if __name__ == '__main__': + path = '/output' + if len(sys.argv) > 1: + path = sys.argv[1] + print(get_extras_packages(path)) diff --git a/docker/python-builder/scripts/install-from-bindep b/docker/python-builder/scripts/install-from-bindep new file mode 100755 index 0000000000..609a666bb2 --- /dev/null +++ b/docker/python-builder/scripts/install-from-bindep @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright (c) 2019 Red Hat, 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. + +set -e + +apt-get update +apt-get -y install $(cat /output/bindep/run.txt) +pip install --cache-dir=/output/wheels /output/wheels/*.whl + +# clean up after ourselves +apt-get clean +rm -rf /var/lib/apt/lists/*