All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images
@ 2016-05-26 14:27 Alex Bennée
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option Alex Bennée
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Alex Bennée @ 2016-05-26 14:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: famz, peter.maydell, neil.williams, steve.mcintyre, riku.voipio,
	Alex Bennée

Hi,

I had a an set of scripts for setting up and running executable
binaries in a $arch-linux-user powered rootfs. However it has been a
while since I last used them and there was various breakage which
spurred me on to a better way. As Fam has been working with Docker
containers for building I investigated if we could use the docker
framework to make all the chroot/bind mount stuff go away.

A couple of caveats as befits an RFC:

  - built on top of Fam's docker series [PATCH v5 00/14] tests: Introducing docker tests
  - assumes binfmt_misc setup an set to /usr/bin/qemu-FOO
  - uses ldd to copy dynamically shared libs across
    - this should be OK for distro's that are multiarch safe

For example to build a Debian Testing armhf distro I run:

  DEB_ARCH=armhf DEB_TYPE=testing \
    ./tests/docker/docker.py build --qemu=qemu-arm debian:armhf \
    ./tests/docker/dockerfiles/debian-bootstrap.docker

This uses Fam's docker helper which now copies qemu-arm into the
docker build context, loads the debian-bootstrap.docker evaluating
HOST_CMD (to do the debootstrap) and then runs the build steps to make
the image. I can then run the final image with something like:

$ docker run -t -i --rm debian:armhf /bin/bash
root@e659ddf8232c:/# uname -a
Linux e659ddf8232c 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37
UTC 2016 armv7l GNU/Linux

Obviously this requires Fam's stuff to go in and needs some clean-up
but is this worth pursuing?

Cheers,


Alex Bennée (2):
  tests/docker/docker.py: support --qemu option
  add debian-bootstrap.docker target

 tests/docker/docker.py                           | 77 +++++++++++++++++++++---
 tests/docker/dockerfiles/debian-bootstrap.docker | 22 +++++++
 2 files changed, 89 insertions(+), 10 deletions(-)
 create mode 100644 tests/docker/dockerfiles/debian-bootstrap.docker

-- 
2.7.4

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option
  2016-05-26 14:27 [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Alex Bennée
@ 2016-05-26 14:27 ` Alex Bennée
  2016-05-27 11:28   ` Fam Zheng
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target Alex Bennée
  2016-05-27 12:27 ` [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Fam Zheng
  2 siblings, 1 reply; 11+ messages in thread
From: Alex Bennée @ 2016-05-26 14:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: famz, peter.maydell, neil.williams, steve.mcintyre, riku.voipio,
	Alex Bennée

When passed the name of a qemu-$arch binary we copy it and any linked
libraries into the docker build context. These can then be included by a
dockerfile with the line:

  # Copy all of context into container
  ADD . /

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/docker/docker.py | 58 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index fe73de7..e9242f3 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -20,6 +20,8 @@ import atexit
 import uuid
 import argparse
 import tempfile
+import re
+from shutil import copyfile
 
 def _text_checksum(text):
     """Calculate a digest string unique to the text content"""
@@ -37,6 +39,27 @@ def _guess_docker_command():
     raise Exception("Cannot find working docker command. Tried:\n%s" % \
                     commands_txt)
 
+def _find_user_binary(binary_name):
+    """ Find a binary in the QEMU source tree. Used for finding qemu-$arch."""
+    top = os.path.abspath("%s/../../.." % sys.argv[0])
+    linux_user = [ x for x in os.listdir(top) if x.endswith("-linux-user") ]
+    for x in linux_user:
+        check_path = "%s/%s/%s" % (top, x, binary_name)
+        if os.path.isfile(check_path):
+            print ("found %s" % check_path)
+            return check_path
+    return None
+
+def _copy_with_mkdir(src, root_dir, sub_path):
+    """Copy src into root_dir, creating sub_path as needed."""
+    full_path = "%s/%s" % (root_dir, sub_path)
+    try:
+        os.makedirs(full_path)
+    except OSError:
+        print "skipping %s" % (full_path)
+
+    copyfile(src, "%s/%s" % (full_path, os.path.basename(src)))
+
 class Docker(object):
     """ Running Docker commands """
     def __init__(self):
@@ -86,18 +109,36 @@ class Docker(object):
         labels = json.loads(resp)[0]["Config"].get("Labels", {})
         return labels.get("com.qemu.dockerfile-checksum", "")
 
-    def build_image(self, tag, dockerfile, df_path, quiet=True, argv=None):
+    def build_image(self, tag, dockerfile, quiet=True, qemu=None, argv=None):
         if argv == None:
             argv = []
+
+        # Create a temporary docker context to build in
+        tmp_dir = tempfile.mkdtemp(prefix="docker_build")
+
+        # Copy the dockerfile into our work space
         tmp = dockerfile + "\n" + \
               "LABEL com.qemu.dockerfile-checksum=%s" % \
               _text_checksum(dockerfile)
-        dirname = os.path.dirname(df_path)
-        tmp_df = tempfile.NamedTemporaryFile(dir=dirname)
+        tmp_df = tempfile.NamedTemporaryFile(dir=tmp_dir, suffix=".docker")
         tmp_df.write(tmp)
         tmp_df.flush()
+
+        # Do we want to copy QEMU into here?
+        if qemu:
+            _copy_with_mkdir(qemu, tmp_dir, "/usr/bin")
+            # do ldd bit here
+            ldd_output = subprocess.check_output(["ldd", qemu])
+            for l in ldd_output.split("\n"):
+                s = re.search("(/.*/)(\S*)", l)
+                if s and len(s.groups())==2:
+                    so_path=s.groups()[0]
+                    so_lib=s.groups()[1]
+                    _copy_with_mkdir("%s/%s" % (so_path, so_lib),
+                                     tmp_dir, so_path)
+
         self._do(["build", "-t", tag, "-f", tmp_df.name] + argv + \
-                 [dirname],
+                 [tmp_dir],
                  quiet=quiet)
 
     def image_matches_dockerfile(self, tag, dockerfile):
@@ -148,6 +189,7 @@ class BuildCommand(SubCommand):
     """ Build docker image out of a dockerfile. Arguments: <tag> <dockerfile>"""
     name = "build"
     def args(self, parser):
+        parser.add_argument("--qemu", help="Include qemu-user binaries")
         parser.add_argument("tag",
                             help="Image Tag")
         parser.add_argument("dockerfile",
@@ -157,14 +199,18 @@ class BuildCommand(SubCommand):
         dockerfile = open(args.dockerfile, "rb").read()
         tag = args.tag
 
+        # find qemu binary
+        qbin=None
+        if args.qemu:
+            qbin=_find_user_binary(args.qemu)
+
         dkr = Docker()
         if dkr.image_matches_dockerfile(tag, dockerfile):
             if not args.quiet:
                 print "Image is up to date."
             return 0
 
-        dkr.build_image(tag, dockerfile, args.dockerfile,
-                        quiet=args.quiet, argv=argv)
+        dkr.build_image(tag, dockerfile, quiet=args.quiet, qemu=qbin, argv=argv)
         return 0
 
 class CleanCommand(SubCommand):
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target
  2016-05-26 14:27 [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Alex Bennée
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option Alex Bennée
@ 2016-05-26 14:27 ` Alex Bennée
  2016-05-27 12:23   ` Fam Zheng
  2016-05-27 12:27 ` [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Fam Zheng
  2 siblings, 1 reply; 11+ messages in thread
From: Alex Bennée @ 2016-05-26 14:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: famz, peter.maydell, neil.williams, steve.mcintyre, riku.voipio,
	Alex Bennée

Together with some changes to the docker script you can now build an
arbitrary architecture of Debian using debootstrap. To achieve this I
introduce the concept of a HOST_CMD in the docker config file. While
copying the file into workspace the HOST_CMD is run in the docker build
context. This allows debootstrap to set up its first stage before the
container is built.

To build a container you need a command line like:

  DEB_ARCH=armhf DEB_TYPE=testing \
    ./tests/docker/docker.py build --qemu=qemu-arm debian:armhf \
    ./tests/docker/dockerfiles/debian-bootstrap.docker

The DEB_ARCH/DEB_TYPE are expanded in the docker file by the HOST_CMD:

  HOST_CMD fakeroot debootstrap --variant=minbase --foreign \
    --arch=$DEB_ARCH $DEB_TYPE . \
    http://httpredir.debian.org/debian

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/docker/docker.py                           | 27 +++++++++++++++++-------
 tests/docker/dockerfiles/debian-bootstrap.docker | 22 +++++++++++++++++++
 2 files changed, 41 insertions(+), 8 deletions(-)
 create mode 100644 tests/docker/dockerfiles/debian-bootstrap.docker

diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index e9242f3..3ba3d4e 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -21,7 +21,7 @@ import uuid
 import argparse
 import tempfile
 import re
-from shutil import copyfile
+from shutil import copy
 
 def _text_checksum(text):
     """Calculate a digest string unique to the text content"""
@@ -46,7 +46,6 @@ def _find_user_binary(binary_name):
     for x in linux_user:
         check_path = "%s/%s/%s" % (top, x, binary_name)
         if os.path.isfile(check_path):
-            print ("found %s" % check_path)
             return check_path
     return None
 
@@ -58,7 +57,7 @@ def _copy_with_mkdir(src, root_dir, sub_path):
     except OSError:
         print "skipping %s" % (full_path)
 
-    copyfile(src, "%s/%s" % (full_path, os.path.basename(src)))
+    copy(src, "%s/%s" % (full_path, os.path.basename(src)))
 
 class Docker(object):
     """ Running Docker commands """
@@ -117,11 +116,23 @@ class Docker(object):
         tmp_dir = tempfile.mkdtemp(prefix="docker_build")
 
         # Copy the dockerfile into our work space
-        tmp = dockerfile + "\n" + \
-              "LABEL com.qemu.dockerfile-checksum=%s" % \
-              _text_checksum(dockerfile)
+        # line by line, stripping and executing HOST_CMDs
+        #
         tmp_df = tempfile.NamedTemporaryFile(dir=tmp_dir, suffix=".docker")
-        tmp_df.write(tmp)
+
+        for l in open(dockerfile).readlines():
+            m = re.match("HOST_CMD ", l)
+            if m:
+                print "l=%s" % (l)
+                cmd = l[m.end():]
+                r = subprocess.check_call(cmd, cwd=tmp_dir, shell=True)
+                tmp_df.write("# HOST_CMD %s# HOST_RES = %d\n" % (cmd, r))
+            else:
+                tmp_df.write(l)
+
+        tmp_df.write("\n")
+        tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" %
+                     _text_checksum(dockerfile))
         tmp_df.flush()
 
         # Do we want to copy QEMU into here?
@@ -210,7 +221,7 @@ class BuildCommand(SubCommand):
                 print "Image is up to date."
             return 0
 
-        dkr.build_image(tag, dockerfile, quiet=args.quiet, qemu=qbin, argv=argv)
+        dkr.build_image(tag, args.dockerfile, quiet=args.quiet, qemu=qbin, argv=argv)
         return 0
 
 class CleanCommand(SubCommand):
diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker
new file mode 100644
index 0000000..44d107d
--- /dev/null
+++ b/tests/docker/dockerfiles/debian-bootstrap.docker
@@ -0,0 +1,22 @@
+# Create Debian Bootstrap Image
+#
+# This is intended to be pre-poluated by:
+#  - a first stage debootstrap
+#  - a native qemu-$arch that binfmt_misc will run
+FROM scratch
+
+# HOST_CMD is executed by docker.py while building the context
+HOST_CMD fakeroot debootstrap --variant=minbase --foreign --arch=$DEB_ARCH $DEB_TYPE . http://httpredir.debian.org/debian
+
+# Add everything from the context into the container
+ADD . /
+
+# Patch all mounts as docker already has stuff set up
+RUN sed -i 's/in_target mount/echo not for docker in_target mount/g' /debootstrap/functions
+
+# Run stage 2
+RUN /debootstrap/debootstrap --second-stage
+
+# At this point we can install additional packages if we want
+#RUN apt-get update
+#RUN apt-get dist-upgrade
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option Alex Bennée
@ 2016-05-27 11:28   ` Fam Zheng
  2016-05-31 15:23     ` Alex Bennée
  0 siblings, 1 reply; 11+ messages in thread
From: Fam Zheng @ 2016-05-27 11:28 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio

On Thu, 05/26 15:27, Alex Bennée wrote:
> When passed the name of a qemu-$arch binary we copy it and any linked
> libraries into the docker build context. These can then be included by a
> dockerfile with the line:
> 
>   # Copy all of context into container
>   ADD . /
> 
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

Looks good in general except a few nitpicks below, most important one being the
binary path lookup.

> ---
>  tests/docker/docker.py | 58 ++++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 52 insertions(+), 6 deletions(-)
> 
> diff --git a/tests/docker/docker.py b/tests/docker/docker.py
> index fe73de7..e9242f3 100755
> --- a/tests/docker/docker.py
> +++ b/tests/docker/docker.py
> @@ -20,6 +20,8 @@ import atexit
>  import uuid
>  import argparse
>  import tempfile
> +import re
> +from shutil import copyfile
>  
>  def _text_checksum(text):
>      """Calculate a digest string unique to the text content"""
> @@ -37,6 +39,27 @@ def _guess_docker_command():
>      raise Exception("Cannot find working docker command. Tried:\n%s" % \
>                      commands_txt)
>  
> +def _find_user_binary(binary_name):
> +    """ Find a binary in the QEMU source tree. Used for finding qemu-$arch."""
> +    top = os.path.abspath("%s/../../.." % sys.argv[0])

What if this is an out of tree build?

> +    linux_user = [ x for x in os.listdir(top) if x.endswith("-linux-user") ]
> +    for x in linux_user:
> +        check_path = "%s/%s/%s" % (top, x, binary_name)

os.path.join()?

> +        if os.path.isfile(check_path):
> +            print ("found %s" % check_path)
> +            return check_path
> +    return None
> +
> +def _copy_with_mkdir(src, root_dir, sub_path):
> +    """Copy src into root_dir, creating sub_path as needed."""
> +    full_path = "%s/%s" % (root_dir, sub_path)
> +    try:
> +        os.makedirs(full_path)
> +    except OSError:
> +        print "skipping %s" % (full_path)

Print the error message too? Also do you want to "return"?

> +
> +    copyfile(src, "%s/%s" % (full_path, os.path.basename(src)))
> +
>  class Docker(object):
>      """ Running Docker commands """
>      def __init__(self):
> @@ -86,18 +109,36 @@ class Docker(object):
>          labels = json.loads(resp)[0]["Config"].get("Labels", {})
>          return labels.get("com.qemu.dockerfile-checksum", "")
>  
> -    def build_image(self, tag, dockerfile, df_path, quiet=True, argv=None):
> +    def build_image(self, tag, dockerfile, quiet=True, qemu=None, argv=None):
>          if argv == None:
>              argv = []
> +
> +        # Create a temporary docker context to build in
> +        tmp_dir = tempfile.mkdtemp(prefix="docker_build")
> +
> +        # Copy the dockerfile into our work space
>          tmp = dockerfile + "\n" + \
>                "LABEL com.qemu.dockerfile-checksum=%s" % \
>                _text_checksum(dockerfile)
> -        dirname = os.path.dirname(df_path)
> -        tmp_df = tempfile.NamedTemporaryFile(dir=dirname)
> +        tmp_df = tempfile.NamedTemporaryFile(dir=tmp_dir, suffix=".docker")
>          tmp_df.write(tmp)
>          tmp_df.flush()
> +
> +        # Do we want to copy QEMU into here?
> +        if qemu:
> +            _copy_with_mkdir(qemu, tmp_dir, "/usr/bin")

Looks like "/usr/bin" is combined with tmp_dir as "FOO//usr/bin", superfluous?

> +            # do ldd bit here
> +            ldd_output = subprocess.check_output(["ldd", qemu])
> +            for l in ldd_output.split("\n"):
> +                s = re.search("(/.*/)(\S*)", l)
> +                if s and len(s.groups())==2:
> +                    so_path=s.groups()[0]
> +                    so_lib=s.groups()[1]
> +                    _copy_with_mkdir("%s/%s" % (so_path, so_lib),
> +                                     tmp_dir, so_path)
> +
>          self._do(["build", "-t", tag, "-f", tmp_df.name] + argv + \
> -                 [dirname],
> +                 [tmp_dir],
>                   quiet=quiet)
>  
>      def image_matches_dockerfile(self, tag, dockerfile):
> @@ -148,6 +189,7 @@ class BuildCommand(SubCommand):
>      """ Build docker image out of a dockerfile. Arguments: <tag> <dockerfile>"""
>      name = "build"
>      def args(self, parser):
> +        parser.add_argument("--qemu", help="Include qemu-user binaries")

Can I ask for a rename of this and the variable names in this patch, to a more
generic name (to reflect that it is inherently orthorgonal to the type of the
binary we are copying)? How about:

           parser.add_argument("--executable-inject", "-e",
                               help="""Specify a binary that will be copied to the
                               container together with all its dependent
                               libraries""")

And I think it is reasonable to expect the user (or the calling Makefile) to
designate a working absolute or relative path, instead of looking up it
ourselves.

>          parser.add_argument("tag",
>                              help="Image Tag")
>          parser.add_argument("dockerfile",
> @@ -157,14 +199,18 @@ class BuildCommand(SubCommand):
>          dockerfile = open(args.dockerfile, "rb").read()
>          tag = args.tag
>  
> +        # find qemu binary
> +        qbin=None

Add whitespaces around "="?

> +        if args.qemu:
> +            qbin=_find_user_binary(args.qemu)

Ditto, and some more occasions above.

> +
>          dkr = Docker()
>          if dkr.image_matches_dockerfile(tag, dockerfile):
>              if not args.quiet:
>                  print "Image is up to date."
>              return 0
>  
> -        dkr.build_image(tag, dockerfile, args.dockerfile,
> -                        quiet=args.quiet, argv=argv)
> +        dkr.build_image(tag, dockerfile, quiet=args.quiet, qemu=qbin, argv=argv)
>          return 0
>  
>  class CleanCommand(SubCommand):
> -- 
> 2.7.4
> 

Thanks,

Fam

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target Alex Bennée
@ 2016-05-27 12:23   ` Fam Zheng
  2016-05-31 15:27     ` Alex Bennée
  0 siblings, 1 reply; 11+ messages in thread
From: Fam Zheng @ 2016-05-27 12:23 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio

On Thu, 05/26 15:27, Alex Bennée wrote:
> Together with some changes to the docker script you can now build an
> arbitrary architecture of Debian using debootstrap. To achieve this I
> introduce the concept of a HOST_CMD in the docker config file. While
> copying the file into workspace the HOST_CMD is run in the docker build
> context. This allows debootstrap to set up its first stage before the
> container is built.

Could you instead introduce the concept of $IMAGE.pre file (in this case
debian-bootstrap.pre, aside debian-bootstrap.docker), and exec it in
docker.py?  It would be much more flexible, and we we wouldn't need to inject a
custom directive to filter it out.

Fam

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images
  2016-05-26 14:27 [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Alex Bennée
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option Alex Bennée
  2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target Alex Bennée
@ 2016-05-27 12:27 ` Fam Zheng
  2 siblings, 0 replies; 11+ messages in thread
From: Fam Zheng @ 2016-05-27 12:27 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio

On Thu, 05/26 15:27, Alex Bennée wrote:
> $ docker run -t -i --rm debian:armhf /bin/bash
> root@e659ddf8232c:/# uname -a
> Linux e659ddf8232c 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37
> UTC 2016 armv7l GNU/Linux
> 
> Obviously this requires Fam's stuff to go in and needs some clean-up
> but is this worth pursuing?

I'm very interested in the feature this brings. Please see my replies to
individual patches regarding some implementation details.

Fam

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option
  2016-05-27 11:28   ` Fam Zheng
@ 2016-05-31 15:23     ` Alex Bennée
  2016-06-01  3:00       ` Fam Zheng
  0 siblings, 1 reply; 11+ messages in thread
From: Alex Bennée @ 2016-05-31 15:23 UTC (permalink / raw)
  To: Fam Zheng
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio


Fam Zheng <famz@redhat.com> writes:

> On Thu, 05/26 15:27, Alex Bennée wrote:
>> When passed the name of a qemu-$arch binary we copy it and any linked
>> libraries into the docker build context. These can then be included by a
>> dockerfile with the line:
>>
>>   # Copy all of context into container
>>   ADD . /
>>
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>
> Looks good in general except a few nitpicks below, most important one being the
> binary path lookup.
>
>> ---
>>  tests/docker/docker.py | 58 ++++++++++++++++++++++++++++++++++++++++++++------
>>  1 file changed, 52 insertions(+), 6 deletions(-)
>>
>> diff --git a/tests/docker/docker.py b/tests/docker/docker.py
>> index fe73de7..e9242f3 100755
>> --- a/tests/docker/docker.py
>> +++ b/tests/docker/docker.py
>> @@ -20,6 +20,8 @@ import atexit
>>  import uuid
>>  import argparse
>>  import tempfile
>> +import re
>> +from shutil import copyfile
>>
>>  def _text_checksum(text):
>>      """Calculate a digest string unique to the text content"""
>> @@ -37,6 +39,27 @@ def _guess_docker_command():
>>      raise Exception("Cannot find working docker command. Tried:\n%s" % \
>>                      commands_txt)
>>
>> +def _find_user_binary(binary_name):
>> +    """ Find a binary in the QEMU source tree. Used for finding qemu-$arch."""
>> +    top = os.path.abspath("%s/../../.." % sys.argv[0])
>
> What if this is an out of tree build?

Yes I kinda avoided the complexity here. Do we have a programatic way of
finding this out or should we just assume we get based a resolvable path?

>
>> +    linux_user = [ x for x in os.listdir(top) if x.endswith("-linux-user") ]
>> +    for x in linux_user:
>> +        check_path = "%s/%s/%s" % (top, x, binary_name)
>
> os.path.join()?

Ack.

>
>> +        if os.path.isfile(check_path):
>> +            print ("found %s" % check_path)
>> +            return check_path
>> +    return None
>> +
>> +def _copy_with_mkdir(src, root_dir, sub_path):
>> +    """Copy src into root_dir, creating sub_path as needed."""
>> +    full_path = "%s/%s" % (root_dir, sub_path)
>> +    try:
>> +        os.makedirs(full_path)
>> +    except OSError:
>> +        print "skipping %s" % (full_path)
>
> Print the error message too? Also do you want to "return"?

It's really a NOP to behave in a mkdir -p type way. Python 3 provides an
exist_ok parameter but I assume we aren't ready to drop python 2 just
yet ;-)

>
>> +
>> +    copyfile(src, "%s/%s" % (full_path, os.path.basename(src)))
>> +
>>  class Docker(object):
>>      """ Running Docker commands """
>>      def __init__(self):
>> @@ -86,18 +109,36 @@ class Docker(object):
>>          labels = json.loads(resp)[0]["Config"].get("Labels", {})
>>          return labels.get("com.qemu.dockerfile-checksum", "")
>>
>> -    def build_image(self, tag, dockerfile, df_path, quiet=True, argv=None):
>> +    def build_image(self, tag, dockerfile, quiet=True, qemu=None, argv=None):
>>          if argv == None:
>>              argv = []
>> +
>> +        # Create a temporary docker context to build in
>> +        tmp_dir = tempfile.mkdtemp(prefix="docker_build")
>> +
>> +        # Copy the dockerfile into our work space
>>          tmp = dockerfile + "\n" + \
>>                "LABEL com.qemu.dockerfile-checksum=%s" % \
>>                _text_checksum(dockerfile)
>> -        dirname = os.path.dirname(df_path)
>> -        tmp_df = tempfile.NamedTemporaryFile(dir=dirname)
>> +        tmp_df = tempfile.NamedTemporaryFile(dir=tmp_dir, suffix=".docker")
>>          tmp_df.write(tmp)
>>          tmp_df.flush()
>> +
>> +        # Do we want to copy QEMU into here?
>> +        if qemu:
>> +            _copy_with_mkdir(qemu, tmp_dir, "/usr/bin")
>
> Looks like "/usr/bin" is combined with tmp_dir as "FOO//usr/bin",
> superfluous?

Yeah, also a bit hacky. In my original shell script it actually searched
the output of Debian's binfmt_update script to see where it was set to
search for the qemu binary from.

>
>> +            # do ldd bit here
>> +            ldd_output = subprocess.check_output(["ldd", qemu])
>> +            for l in ldd_output.split("\n"):
>> +                s = re.search("(/.*/)(\S*)", l)
>> +                if s and len(s.groups())==2:
>> +                    so_path=s.groups()[0]
>> +                    so_lib=s.groups()[1]
>> +                    _copy_with_mkdir("%s/%s" % (so_path, so_lib),
>> +                                     tmp_dir, so_path)
>> +
>>          self._do(["build", "-t", tag, "-f", tmp_df.name] + argv + \
>> -                 [dirname],
>> +                 [tmp_dir],
>>                   quiet=quiet)
>>
>>      def image_matches_dockerfile(self, tag, dockerfile):
>> @@ -148,6 +189,7 @@ class BuildCommand(SubCommand):
>>      """ Build docker image out of a dockerfile. Arguments: <tag> <dockerfile>"""
>>      name = "build"
>>      def args(self, parser):
>> +        parser.add_argument("--qemu", help="Include qemu-user binaries")
>
> Can I ask for a rename of this and the variable names in this patch, to a more
> generic name (to reflect that it is inherently orthorgonal to the type of the
> binary we are copying)? How about:
>
>            parser.add_argument("--executable-inject", "-e",
>                                help="""Specify a binary that will be copied to the
>                                container together with all its dependent
>                                libraries""")
>
> And I think it is reasonable to expect the user (or the calling Makefile) to
> designate a working absolute or relative path, instead of looking up it
> ourselves.

Makes sense.

>
>>          parser.add_argument("tag",
>>                              help="Image Tag")
>>          parser.add_argument("dockerfile",
>> @@ -157,14 +199,18 @@ class BuildCommand(SubCommand):
>>          dockerfile = open(args.dockerfile, "rb").read()
>>          tag = args.tag
>>
>> +        # find qemu binary
>> +        qbin=None
>
> Add whitespaces around "="?

Ack.

>
>> +        if args.qemu:
>> +            qbin=_find_user_binary(args.qemu)
>
> Ditto, and some more occasions above.

Ack.

>
>> +
>>          dkr = Docker()
>>          if dkr.image_matches_dockerfile(tag, dockerfile):
>>              if not args.quiet:
>>                  print "Image is up to date."
>>              return 0
>>
>> -        dkr.build_image(tag, dockerfile, args.dockerfile,
>> -                        quiet=args.quiet, argv=argv)
>> +        dkr.build_image(tag, dockerfile, quiet=args.quiet, qemu=qbin, argv=argv)
>>          return 0
>>
>>  class CleanCommand(SubCommand):
>> --
>> 2.7.4
>>
>
> Thanks,
>
> Fam


--
Alex Bennée

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target
  2016-05-27 12:23   ` Fam Zheng
@ 2016-05-31 15:27     ` Alex Bennée
  2016-06-01  1:47       ` Fam Zheng
  2016-06-01  4:27       ` [Qemu-devel] [RFC PATCH] docker: Support ".pre" script when building image Fam Zheng
  0 siblings, 2 replies; 11+ messages in thread
From: Alex Bennée @ 2016-05-31 15:27 UTC (permalink / raw)
  To: Fam Zheng
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio


Fam Zheng <famz@redhat.com> writes:

> On Thu, 05/26 15:27, Alex Bennée wrote:
>> Together with some changes to the docker script you can now build an
>> arbitrary architecture of Debian using debootstrap. To achieve this I
>> introduce the concept of a HOST_CMD in the docker config file. While
>> copying the file into workspace the HOST_CMD is run in the docker build
>> context. This allows debootstrap to set up its first stage before the
>> container is built.
>
> Could you instead introduce the concept of $IMAGE.pre file (in this case
> debian-bootstrap.pre, aside debian-bootstrap.docker), and exec it in
> docker.py?  It would be much more flexible, and we we wouldn't need to inject a
> custom directive to filter it out.

I'm ambivalent about that. To be honest this is a bit of a gap in
docker's image creation (or if there is a better more docker-y way of
doing things I couldn't find it). It does have the benefit of keeping
everything in one place.

We are copying the dockerfile to the build environment anyway so it
seemed natural to do the operation while copying across. How would
envison the .pre setup? A #! script we just exec inside the temp
directory we create?

>
> Fam


--
Alex Bennée

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target
  2016-05-31 15:27     ` Alex Bennée
@ 2016-06-01  1:47       ` Fam Zheng
  2016-06-01  4:27       ` [Qemu-devel] [RFC PATCH] docker: Support ".pre" script when building image Fam Zheng
  1 sibling, 0 replies; 11+ messages in thread
From: Fam Zheng @ 2016-06-01  1:47 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio

On Tue, 05/31 16:27, Alex Bennée wrote:
> 
> Fam Zheng <famz@redhat.com> writes:
> 
> > On Thu, 05/26 15:27, Alex Bennée wrote:
> >> Together with some changes to the docker script you can now build an
> >> arbitrary architecture of Debian using debootstrap. To achieve this I
> >> introduce the concept of a HOST_CMD in the docker config file. While
> >> copying the file into workspace the HOST_CMD is run in the docker build
> >> context. This allows debootstrap to set up its first stage before the
> >> container is built.
> >
> > Could you instead introduce the concept of $IMAGE.pre file (in this case
> > debian-bootstrap.pre, aside debian-bootstrap.docker), and exec it in
> > docker.py?  It would be much more flexible, and we we wouldn't need to inject a
> > custom directive to filter it out.
> 
> I'm ambivalent about that. To be honest this is a bit of a gap in
> docker's image creation (or if there is a better more docker-y way of
> doing things I couldn't find it). It does have the benefit of keeping
> everything in one place.

I don't like that it pollutes the dockerfile, rendering it invalid for raw
"docker build", which is not very good.

> 
> We are copying the dockerfile to the build environment anyway so it
> seemed natural to do the operation while copying across. How would
> envison the .pre setup? A #! script we just exec inside the temp
> directory we create?

Yes, since I'm going to send a v7, I'll merge the tmp dir part in to my series,
and add a new patch for the "pre" idea to see if we like it.

Fam

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option
  2016-05-31 15:23     ` Alex Bennée
@ 2016-06-01  3:00       ` Fam Zheng
  0 siblings, 0 replies; 11+ messages in thread
From: Fam Zheng @ 2016-06-01  3:00 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, peter.maydell, neil.williams, steve.mcintyre, riku.voipio

On Tue, 05/31 16:23, Alex Bennée wrote:
> >> +def _find_user_binary(binary_name):
> >> +    """ Find a binary in the QEMU source tree. Used for finding qemu-$arch."""
> >> +    top = os.path.abspath("%s/../../.." % sys.argv[0])
> >
> > What if this is an out of tree build?
> 
> Yes I kinda avoided the complexity here. Do we have a programatic way of
> finding this out or should we just assume we get based a resolvable path?

As said below, let's assume the user provides an absolute path or a relative
path against the working directory, so we don't need to worry about path
guessing. The script caller should have more information.

Fam

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [Qemu-devel] [RFC PATCH] docker: Support ".pre" script when building image
  2016-05-31 15:27     ` Alex Bennée
  2016-06-01  1:47       ` Fam Zheng
@ 2016-06-01  4:27       ` Fam Zheng
  1 sibling, 0 replies; 11+ messages in thread
From: Fam Zheng @ 2016-06-01  4:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fam Zheng, alex.bennee

When building "foo.docker", if a "foo.pre" script exists, it will be
executed in the building context before "docker build" is invoked.

Signed-off-by: Fam Zheng <famz@redhat.com>
---
 tests/docker/docker.py | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index 0151362..7b8f022 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -99,6 +99,12 @@ class Docker(object):
         tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" %
                      _text_checksum(dockerfile))
         tmp_df.flush()
+        pre_path = os.path.abspath(df_path[:-len(".docker")] + ".pre")
+        if os.path.isfile(pre_path):
+            if quiet:
+                subprocess.check_output([pre_path], cwd=tmp_dir)
+            else:
+                subprocess.check_call([pre_path], cwd=tmp_dir)
         self._do(["build", "-t", tag, "-f", tmp_df.name] + argv + \
                  [tmp_dir],
                  quiet=quiet)
-- 
2.8.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-06-01  4:27 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-26 14:27 [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Alex Bennée
2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 1/2] tests/docker/docker.py: support --qemu option Alex Bennée
2016-05-27 11:28   ` Fam Zheng
2016-05-31 15:23     ` Alex Bennée
2016-06-01  3:00       ` Fam Zheng
2016-05-26 14:27 ` [Qemu-devel] [RFC PATCH 2/2] add debian-bootstrap.docker target Alex Bennée
2016-05-27 12:23   ` Fam Zheng
2016-05-31 15:27     ` Alex Bennée
2016-06-01  1:47       ` Fam Zheng
2016-06-01  4:27       ` [Qemu-devel] [RFC PATCH] docker: Support ".pre" script when building image Fam Zheng
2016-05-27 12:27 ` [Qemu-devel] [RFC PATCH 0/2] Support building qemu-user powered docker test images Fam Zheng

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.