kopia lustrzana https://github.com/jupyterhub/repo2docker
117 wiersze
3.8 KiB
Python
117 wiersze
3.8 KiB
Python
import os
|
|
import sys
|
|
import subprocess
|
|
|
|
import docker
|
|
|
|
from traitlets import Unicode, Dict
|
|
from traitlets.config import LoggingConfigurable
|
|
|
|
import logging
|
|
from pythonjsonlogger import jsonlogger
|
|
|
|
from .utils import execute_cmd
|
|
|
|
|
|
class BuildPack(LoggingConfigurable):
|
|
name = Unicode()
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
# FIXME: Not sure why this needs to be repeated - shouldn't configuring Application be enough?
|
|
logHandler = logging.StreamHandler()
|
|
formatter = jsonlogger.JsonFormatter()
|
|
logHandler.setFormatter(formatter)
|
|
# Need to reset existing handlers, or we repeat messages
|
|
self.log.handlers = []
|
|
self.log.addHandler(logHandler)
|
|
self.log.setLevel(logging.INFO)
|
|
|
|
def detect(self, workdir):
|
|
"""
|
|
Return True if app in workdir can be built with this buildpack
|
|
"""
|
|
pass
|
|
|
|
def build(self, workdir, ref, output_image_spec):
|
|
"""
|
|
Run a command that will take workdir and produce an image ready to be pushed
|
|
"""
|
|
pass
|
|
|
|
|
|
class DockerBuildPack(BuildPack):
|
|
name = Unicode('Dockerfile')
|
|
def detect(self, workdir):
|
|
return os.path.exists(os.path.join(workdir, 'Dockerfile'))
|
|
|
|
def build(self, workdir, ref, output_image_spec):
|
|
client = docker.APIClient(base_url='unix://var/run/docker.sock', version='auto')
|
|
for progress in client.build(
|
|
path=workdir,
|
|
tag=output_image_spec,
|
|
decode=True
|
|
):
|
|
if 'stream' in progress:
|
|
self.log.info(progress['stream'].rstrip(), extra=dict(phase='building'))
|
|
|
|
|
|
class S2IBuildPack(BuildPack):
|
|
def s2i_build(self, workdir, ref, output_image_spec, build_image):
|
|
# Note: Ideally we'd just copy from workdir here, rather than clone and check out again
|
|
# However, setting just --copy and not specifying a ref seems to check out master for
|
|
# some reason. Investigate deeper FIXME
|
|
cmd = [
|
|
's2i',
|
|
'build',
|
|
'--exclude', '""',
|
|
'--ref', ref,
|
|
workdir,
|
|
build_image,
|
|
output_image_spec,
|
|
]
|
|
try:
|
|
for line in execute_cmd(cmd):
|
|
self.log.info(line, extra=dict(phase='building', builder=self.name))
|
|
except subprocess.CalledProcessError:
|
|
self.log.error('Failed to build image!', extra=dict(phase='failed'))
|
|
sys.exit(1)
|
|
|
|
|
|
class CondaBuildPack(S2IBuildPack):
|
|
"""Build Pack for installing from a conda environment.yml using S2I"""
|
|
|
|
name = Unicode('conda')
|
|
build_image = Unicode('jupyterhub/singleuser-builder-conda:v0.1.5', config=True)
|
|
|
|
def detect(self, workdir):
|
|
return os.path.exists(os.path.join(workdir, 'environment.yml'))
|
|
|
|
def build(self, workdir, ref, output_image_spec):
|
|
return self.s2i_build(workdir, ref, output_image_spec, self.build_image)
|
|
|
|
|
|
class PythonBuildPack(S2IBuildPack):
|
|
"""Build Pack for installing from a pip requirements.txt using S2I"""
|
|
name = Unicode('python-pip')
|
|
runtime_builder_map = Dict({
|
|
'python-2.7': 'jupyterhub/singleuser-builder-venv-2.7:v0.1.5',
|
|
'python-3.5': 'jupyterhub/singleuser-builder-venv-3.5:v0.1.5',
|
|
})
|
|
|
|
runtime = Unicode(
|
|
'python-3.5',
|
|
config=True
|
|
)
|
|
|
|
def detect(self, workdir):
|
|
if os.path.exists(os.path.join(workdir, 'requirements.txt')):
|
|
try:
|
|
with open(os.path.join(workdir, 'runtime.txt')) as f:
|
|
self.runtime = f.read().strip()
|
|
except FileNotFoundError:
|
|
pass
|
|
return True
|
|
|
|
def build(self, workdir, ref, output_image_spec):
|
|
return self.s2i_build(workdir, ref, output_image_spec, self.runtime_builder_map[self.runtime])
|