kopia lustrzana https://github.com/jupyterhub/repo2docker
Merge pull request #681 from Xarthisius/abspath_in_scripts
[MRG] Allow absolute paths in build_script_files. Fixes #673pull/782/head
commit
4f428c3055
|
@ -5,8 +5,10 @@ import io
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import docker
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
import hashlib
|
||||||
|
import escapism
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from traitlets import Dict
|
from traitlets import Dict
|
||||||
|
@ -534,6 +536,17 @@ class BuildPack:
|
||||||
"RUN {}".format(textwrap.dedent(script.strip("\n")))
|
"RUN {}".format(textwrap.dedent(script.strip("\n")))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Based on a physical location of a build script on the host,
|
||||||
|
# create a mapping between:
|
||||||
|
# 1. Location of a build script in a Docker build context
|
||||||
|
# ('assemble_files/<escaped-file-path-truncated>-<6-chars-of-its-hash>')
|
||||||
|
# 2. Location of the aforemention script in the Docker image
|
||||||
|
# Base template basically does: COPY <1.> <2.>
|
||||||
|
build_script_files = {
|
||||||
|
self.generate_build_context_filename(k)[0]: v
|
||||||
|
for k, v in self.get_build_script_files().items()
|
||||||
|
}
|
||||||
|
|
||||||
return t.render(
|
return t.render(
|
||||||
packages=sorted(self.get_packages()),
|
packages=sorted(self.get_packages()),
|
||||||
path=self.get_path(),
|
path=self.get_path(),
|
||||||
|
@ -544,13 +557,42 @@ class BuildPack:
|
||||||
preassemble_script_files=self.get_preassemble_script_files(),
|
preassemble_script_files=self.get_preassemble_script_files(),
|
||||||
preassemble_script_directives=preassemble_script_directives,
|
preassemble_script_directives=preassemble_script_directives,
|
||||||
assemble_script_directives=assemble_script_directives,
|
assemble_script_directives=assemble_script_directives,
|
||||||
build_script_files=self.get_build_script_files(),
|
build_script_files=build_script_files,
|
||||||
base_packages=sorted(self.get_base_packages()),
|
base_packages=sorted(self.get_base_packages()),
|
||||||
post_build_scripts=self.get_post_build_scripts(),
|
post_build_scripts=self.get_post_build_scripts(),
|
||||||
start_script=self.get_start_script(),
|
start_script=self.get_start_script(),
|
||||||
appendix=self.appendix,
|
appendix=self.appendix,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_build_context_filename(src_path, hash_length=6):
|
||||||
|
"""
|
||||||
|
Generate a filename for a file injected into the Docker build context.
|
||||||
|
|
||||||
|
In case the src_path is relative, it's assumed it's relative to directory of
|
||||||
|
this __file__. Returns the resulting filename and an absolute path to the source
|
||||||
|
file on host.
|
||||||
|
"""
|
||||||
|
if not os.path.isabs(src_path):
|
||||||
|
src_parts = src_path.split("/")
|
||||||
|
src_path = os.path.join(os.path.dirname(__file__), *src_parts)
|
||||||
|
|
||||||
|
src_path_hash = hashlib.sha256(src_path.encode("utf-8")).hexdigest()
|
||||||
|
safe_chars = set(string.ascii_letters + string.digits)
|
||||||
|
|
||||||
|
def escape(s):
|
||||||
|
return escapism.escape(s, safe=safe_chars, escape_char="-")
|
||||||
|
|
||||||
|
src_path_slug = escape(src_path)
|
||||||
|
filename = "build_script_files/{name}-{hash}"
|
||||||
|
return (
|
||||||
|
filename.format(
|
||||||
|
name=src_path_slug[: 255 - hash_length - 20],
|
||||||
|
hash=src_path_hash[:hash_length],
|
||||||
|
).lower(),
|
||||||
|
src_path,
|
||||||
|
)
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self,
|
||||||
client,
|
client,
|
||||||
|
@ -580,9 +622,8 @@ class BuildPack:
|
||||||
return tar
|
return tar
|
||||||
|
|
||||||
for src in sorted(self.get_build_script_files()):
|
for src in sorted(self.get_build_script_files()):
|
||||||
src_parts = src.split("/")
|
dest_path, src_path = self.generate_build_context_filename(src)
|
||||||
src_path = os.path.join(os.path.dirname(__file__), *src_parts)
|
tar.add(src_path, dest_path, filter=_filter_tar)
|
||||||
tar.add(src_path, src, filter=_filter_tar)
|
|
||||||
|
|
||||||
tar.add(ENTRYPOINT_FILE, "repo2docker-entrypoint", filter=_filter_tar)
|
tar.add(ENTRYPOINT_FILE, "repo2docker-entrypoint", filter=_filter_tar)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""Test if assemble scripts from outside of r2d repo are accepted."""
|
||||||
|
import time
|
||||||
|
from repo2docker.app import Repo2Docker
|
||||||
|
from repo2docker.buildpacks import PythonBuildPack
|
||||||
|
|
||||||
|
|
||||||
|
def test_Repo2Docker_external_build_scripts(tmpdir):
|
||||||
|
tempfile = tmpdir.join("absolute-script")
|
||||||
|
tempfile.write("Hello World of Absolute Paths!")
|
||||||
|
|
||||||
|
class MockBuildPack(PythonBuildPack):
|
||||||
|
def detect(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_build_script_files(self):
|
||||||
|
files = {str(tempfile): "/tmp/my_extra_script"}
|
||||||
|
files.update(super().get_build_script_files())
|
||||||
|
return files
|
||||||
|
|
||||||
|
app = Repo2Docker(repo=str(tmpdir))
|
||||||
|
app.buildpacks = [MockBuildPack]
|
||||||
|
app.initialize()
|
||||||
|
app.build()
|
||||||
|
container = app.start_container()
|
||||||
|
|
||||||
|
# give the container a chance to start
|
||||||
|
tic = 180
|
||||||
|
while container.status != "running" or tic < 0:
|
||||||
|
time.sleep(1)
|
||||||
|
tic -= 1
|
||||||
|
|
||||||
|
assert container.status == "running"
|
||||||
|
|
||||||
|
try:
|
||||||
|
status, output = container.exec_run(["sh", "-c", "cat /tmp/my_extra_script"])
|
||||||
|
assert status == 0
|
||||||
|
assert output.decode("utf-8") == "Hello World of Absolute Paths!"
|
||||||
|
finally:
|
||||||
|
container.stop(timeout=1)
|
||||||
|
container.reload()
|
||||||
|
assert container.status == "exited", container.status
|
||||||
|
container.remove()
|
Ładowanie…
Reference in New Issue