From f1e6dce047d091bc4b4ea9264a29401f39381fa6 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Thu, 21 Nov 2013 16:19:19 -0700 Subject: [PATCH 01/23] Update test scripts to always run ALL tests, even when some fail --- hack/make/dyntest | 39 ++++++++++++++++++++++++++++----------- hack/make/test | 37 +++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/hack/make/dyntest b/hack/make/dyntest index ff607d3910..756b038207 100644 --- a/hack/make/dyntest +++ b/hack/make/dyntest @@ -19,18 +19,35 @@ fi bundle_test() { { date - for test_dir in $(find_test_dirs); do ( - set -x - cd $test_dir + + TESTS_FAILED=() + for test_dir in $(find_test_dirs); do + echo - # Install packages that are dependencies of the tests. - # Note: Does not run the tests. - go test -i -ldflags "$LDFLAGS" $BUILDFLAGS - - # Run the tests with the optional $TESTFLAGS. - export TEST_DOCKERINIT_PATH=$DEST/../dynbinary/dockerinit-$VERSION - go test -v -ldflags "$LDFLAGS -X github.com/dotcloud/docker/utils.INITSHA1 \"$DOCKER_INITSHA1\"" $BUILDFLAGS $TESTFLAGS - ) done + if ! ( + set -x + cd $test_dir + + # Install packages that are dependencies of the tests. + # Note: Does not run the tests. + go test -i -ldflags "$LDFLAGS" $BUILDFLAGS + + # Run the tests with the optional $TESTFLAGS. + export TEST_DOCKERINIT_PATH=$DEST/../dynbinary/dockerinit-$VERSION + go test -v -ldflags "$LDFLAGS -X github.com/dotcloud/docker/utils.INITSHA1 \"$DOCKER_INITSHA1\"" $BUILDFLAGS $TESTFLAGS + ); then + TESTS_FAILED+=("$test_dir") + sleep 1 # give it a second, so observers watching can take note + fi + done + + # if some tests fail, we want the bundlescript to fail, but we want to + # try running ALL the tests first, hence TESTS_FAILED + if [ "${#TESTS_FAILED[@]}" -gt 0 ]; then + echo + echo "Test failures in: ${TESTS_FAILED[@]}" + false + fi } 2>&1 | tee $DEST/test.log } diff --git a/hack/make/test b/hack/make/test index 45ffe87fbd..361f731d70 100644 --- a/hack/make/test +++ b/hack/make/test @@ -13,17 +13,34 @@ set -e bundle_test() { { date - for test_dir in $(find_test_dirs); do ( - set -x - cd $test_dir + + TESTS_FAILED=() + for test_dir in $(find_test_dirs); do + echo - # Install packages that are dependencies of the tests. - # Note: Does not run the tests. - go test -i -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS - - # Run the tests with the optional $TESTFLAGS. - go test -v -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS $TESTFLAGS - ) done + if ! ( + set -x + cd $test_dir + + # Install packages that are dependencies of the tests. + # Note: Does not run the tests. + go test -i -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS + + # Run the tests with the optional $TESTFLAGS. + go test -v -ldflags "$LDFLAGS $LDFLAGS_STATIC" $BUILDFLAGS $TESTFLAGS + ); then + TESTS_FAILED+=("$test_dir") + sleep 1 # give it a second, so observers watching can take note + fi + done + + # if some tests fail, we want the bundlescript to fail, but we want to + # try running ALL the tests first, hence TESTS_FAILED + if [ "${#TESTS_FAILED[@]}" -gt 0 ]; then + echo + echo "Test failures in: ${TESTS_FAILED[@]}" + false + fi } 2>&1 | tee $DEST/test.log } From 96b5be9dd9606a9bfbf0fdbe98bcaf8b6e77e4b1 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 14 Nov 2013 15:19:31 +1000 Subject: [PATCH 02/23] add more searchable info to the error message when ADD tries to go outside the context --- buildfile.go | 2 +- integration/buildfile_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildfile.go b/buildfile.go index ce157302f6..b643af509d 100644 --- a/buildfile.go +++ b/buildfile.go @@ -288,7 +288,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error { destPath = destPath + "/" } if !strings.HasPrefix(origPath, b.context) { - return fmt.Errorf("Forbidden path: %s", origPath) + return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath) } fi, err := os.Stat(origPath) if err != nil { diff --git a/integration/buildfile_test.go b/integration/buildfile_test.go index 964b58403b..20d0450a7c 100644 --- a/integration/buildfile_test.go +++ b/integration/buildfile_test.go @@ -483,7 +483,7 @@ func TestForbiddenContextPath(t *testing.T) { t.Fail() } - if err.Error() != "Forbidden path: /" { + if err.Error() != "Forbidden path outside the build context: ../../ (/)" { t.Logf("Error message is not expected: %s", err.Error()) t.Fail() } From f16c45f8b0447249074a0b41807a979a3ae326c5 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Sun, 24 Nov 2013 20:00:39 -0700 Subject: [PATCH 03/23] Add space-escaping to path parts of lxc.mount.entry lines in generated lxc.conf, allowing for spaces in mount point names Fixes #2802 --- lxc_template.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lxc_template.go b/lxc_template.go index 2ba2867428..2c68b7a837 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -1,6 +1,7 @@ package docker import ( + "strings" "text/template" ) @@ -31,8 +32,8 @@ lxc.rootfs = {{$ROOTFS}} {{if and .HostnamePath .HostsPath}} # enable domain name support -lxc.mount.entry = {{.HostnamePath}} {{$ROOTFS}}/etc/hostname none bind,ro 0 0 -lxc.mount.entry = {{.HostsPath}} {{$ROOTFS}}/etc/hosts none bind,ro 0 0 +lxc.mount.entry = {{escapeFstabSpaces .HostnamePath}} {{escapeFstabSpaces $ROOTFS}}/etc/hostname none bind,ro 0 0 +lxc.mount.entry = {{escapeFstabSpaces .HostsPath}} {{escapeFstabSpaces $ROOTFS}}/etc/hosts none bind,ro 0 0 {{end}} # use a dedicated pts for the container (and limit the number of pseudo terminal @@ -84,27 +85,27 @@ lxc.cgroup.devices.allow = c 10:200 rwm lxc.pivotdir = lxc_putold # WARNING: procfs is a known attack vector and should probably be disabled # if your userspace allows it. eg. see http://blog.zx2c4.com/749 -lxc.mount.entry = proc {{$ROOTFS}}/proc proc nosuid,nodev,noexec 0 0 +lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0 # WARNING: sysfs is a known attack vector and should probably be disabled # if your userspace allows it. eg. see http://bit.ly/T9CkqJ -lxc.mount.entry = sysfs {{$ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0 -lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 -#lxc.mount.entry = varrun {{$ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0 -#lxc.mount.entry = varlock {{$ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0 -lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 +lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0 +lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 +#lxc.mount.entry = varrun {{escapeFstabSpaces $ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0 +#lxc.mount.entry = varlock {{escapeFstabSpaces $ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0 +lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 # Inject dockerinit -lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/.dockerinit none bind,ro 0 0 +lxc.mount.entry = {{escapeFstabSpaces .SysInitPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerinit none bind,ro 0 0 # Inject env -lxc.mount.entry = {{.EnvConfigPath}} {{$ROOTFS}}/.dockerenv none bind,ro 0 0 +lxc.mount.entry = {{escapeFstabSpaces .EnvConfigPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerenv none bind,ro 0 0 # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container -lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0 +lxc.mount.entry = {{escapeFstabSpaces .ResolvConfPath}} {{escapeFstabSpaces $ROOTFS}}/etc/resolv.conf none bind,ro 0 0 {{if .Volumes}} {{ $rw := .VolumesRW }} {{range $virtualPath, $realPath := .Volumes}} -lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,{{ if index $rw $virtualPath }}rw{{else}}ro{{end}} 0 0 +lxc.mount.entry = {{escapeFstabSpaces $realPath}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $virtualPath}} none bind,{{ if index $rw $virtualPath }}rw{{else}}ro{{end}} 0 0 {{end}} {{end}} @@ -144,6 +145,12 @@ lxc.cgroup.cpu.shares = {{.Config.CpuShares}} var LxcTemplateCompiled *template.Template +// Escape spaces in strings according to the fstab documentation, which is the +// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab". +func escapeFstabSpaces(field string) string { + return strings.Replace(field, " ", "\\040", -1) +} + func getMemorySwap(config *Config) int64 { // By default, MemorySwap is set to twice the size of RAM. // If you want to omit MemorySwap, set it to `-1'. @@ -167,6 +174,7 @@ func init() { "getMemorySwap": getMemorySwap, "getHostConfig": getHostConfig, "getCapabilities": getCapabilities, + "escapeFstabSpaces": escapeFstabSpaces, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) if err != nil { From b702edadb7d63feb1f40ebfae27a4d745d4b733e Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Sun, 24 Nov 2013 20:02:06 -0700 Subject: [PATCH 04/23] Format lxc_template.go with gofmt --- lxc_template.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxc_template.go b/lxc_template.go index 2c68b7a837..2d95a2971d 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -171,9 +171,9 @@ func getCapabilities(container *Container) *Capabilities { func init() { var err error funcMap := template.FuncMap{ - "getMemorySwap": getMemorySwap, - "getHostConfig": getHostConfig, - "getCapabilities": getCapabilities, + "getMemorySwap": getMemorySwap, + "getHostConfig": getHostConfig, + "getCapabilities": getCapabilities, "escapeFstabSpaces": escapeFstabSpaces, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) From 1af6ffb9bb5a1dec665a56b1bdf621fd2e2b7377 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Tue, 26 Nov 2013 00:07:59 -0700 Subject: [PATCH 05/23] Add explicit test strings for new escapeFstabSpaces function --- lxc_template_unit_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lxc_template_unit_test.go b/lxc_template_unit_test.go index ce5af1d321..ccdfec8890 100644 --- a/lxc_template_unit_test.go +++ b/lxc_template_unit_test.go @@ -100,3 +100,21 @@ func grepFile(t *testing.T, path string, pattern string) { } t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path) } + +func TestEscapeFstabSpaces(t *testing.T) { + var testInputs = map[string]string{ + " ": "\\040", + "": "", + "/double space": "/double\\040\\040space", + "/some long test string": "/some\\040long\\040test\\040string", + "/var/lib/docker": "/var/lib/docker", + " leading": "\\040leading", + "trailing ": "trailing\\040", + } + for in, exp := range testInputs { + if out := escapeFstabSpaces(in); exp != out { + t.Logf("Expected %s got %s", exp, out) + t.Fail() + } + } +} From 82674372946b431f93add255e137f82822eecfc1 Mon Sep 17 00:00:00 2001 From: Yurii Rashkovskii Date: Tue, 26 Nov 2013 06:54:46 -0800 Subject: [PATCH 06/23] Fix command line help for docker save `docker save IMAGE DESTINATION` is not what `docker save` expects --- commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands.go b/commands.go index d992db2e6c..f298e41d1b 100644 --- a/commands.go +++ b/commands.go @@ -2195,7 +2195,7 @@ func (cli *DockerCli) CmdCp(args ...string) error { } func (cli *DockerCli) CmdSave(args ...string) error { - cmd := cli.Subcmd("save", "IMAGE DESTINATION", "Save an image to a tar archive") + cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to stdout)") if err := cmd.Parse(args); err != nil { return err } From 8398abf0dcda8fa7818b69355af9b99ab4e8bdfc Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Tue, 26 Nov 2013 09:36:46 -0700 Subject: [PATCH 07/23] Fix CHANGELOG: we ended up not merging the btrfs driver for last night's release --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75dd094bd4..bdd91eaddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ #### Notable features since 0.6.0 -* Storage drivers: choose from aufs, device mapper, vfs or btrfs. -* Standard Linux support: docker now runs on unmodified linux kernels and all major distributions. +* Storage drivers: choose from aufs, device-mapper, or vfs. +* Standard Linux support: docker now runs on unmodified Linux kernels and all major distributions. * Links: compose complex software stacks by connecting containers to each other. * Container naming: organize your containers by giving them memorable names. * Advanced port redirects: specify port redirects per interface, or keep sensitive ports private. From d370a889c37b762a8ddf7e29a8e666fd7cedec3d Mon Sep 17 00:00:00 2001 From: dkumor Date: Tue, 26 Nov 2013 20:00:13 -0600 Subject: [PATCH 08/23] Deleted references to AUFS AUFS is no longer a dependency (both lxc-docker and lxc-docker-git are >=0.7), and the Arch kernel doesn't need to be replaced with AUFS_friendly. --- docs/sources/installation/archlinux.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/sources/installation/archlinux.rst b/docs/sources/installation/archlinux.rst index d6dc239253..d34dc6a209 100644 --- a/docs/sources/installation/archlinux.rst +++ b/docs/sources/installation/archlinux.rst @@ -30,7 +30,6 @@ either AUR package. * bridge-utils * go * iproute2 -* linux-aufs_friendly * lxc Installation @@ -41,9 +40,6 @@ The instructions here assume **yaourt** is installed. See for information on building and installing packages from the AUR if you have not done so before. -Keep in mind that if **linux-aufs_friendly** is not already installed that a -new kernel will be compiled and this can take quite a while. - :: yaourt -S lxc-docker-git @@ -52,9 +48,6 @@ new kernel will be compiled and this can take quite a while. Starting Docker --------------- -Prior to starting docker modify your bootloader to use the -**linux-aufs_friendly** kernel and reboot your system. - There is a systemd service unit created for docker. To start the docker service: :: From b3e8ba19085732d9f633e4cbddb660f5f8dc5462 Mon Sep 17 00:00:00 2001 From: dkumor Date: Tue, 26 Nov 2013 22:07:56 -0600 Subject: [PATCH 09/23] Arch docs: Updated dependencies to match AUR The AUR packages lxc-docker and lxc-docker-git have changed their dependencies. --- docs/sources/installation/archlinux.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/installation/archlinux.rst b/docs/sources/installation/archlinux.rst index d34dc6a209..e415295918 100644 --- a/docs/sources/installation/archlinux.rst +++ b/docs/sources/installation/archlinux.rst @@ -26,8 +26,8 @@ Dependencies Docker depends on several packages which are specified as dependencies in either AUR package. -* aufs3 * bridge-utils +* device-mapper * go * iproute2 * lxc From 45b1e8c2362b3626ccc564496e3093386bb89cd9 Mon Sep 17 00:00:00 2001 From: Bruno Bigras Date: Tue, 26 Nov 2013 16:11:08 -0500 Subject: [PATCH 10/23] Update postgresql's version in example It seems ppa:pitti/postgresql will be deprecated and only apt.postgresql.org has 9.3. --- docs/sources/examples/postgresql_service.rst | 30 +++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/sources/examples/postgresql_service.rst b/docs/sources/examples/postgresql_service.rst index 3649775485..82ca8b59ca 100644 --- a/docs/sources/examples/postgresql_service.rst +++ b/docs/sources/examples/postgresql_service.rst @@ -45,19 +45,21 @@ Install ``python-software-properties``. apt-get -y install python-software-properties apt-get -y install software-properties-common -Add Pitti's PostgreSQL repository. It contains the most recent stable release -of PostgreSQL i.e. ``9.2``. +Add PostgreSQL's repository. It contains the most recent stable release +of PostgreSQL i.e. ``9.3``. .. code-block:: bash - add-apt-repository ppa:pitti/postgresql + apt-get -y install wget + wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - + echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list apt-get update -Finally, install PostgreSQL 9.2 +Finally, install PostgreSQL 9.3 .. code-block:: bash - apt-get -y install postgresql-9.2 postgresql-client-9.2 postgresql-contrib-9.2 + apt-get -y install postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3 Now, create a PostgreSQL superuser role that can create databases and other roles. Following Vagrant's convention the role will be named @@ -76,14 +78,14 @@ role. Adjust PostgreSQL configuration so that remote connections to the database are possible. Make sure that inside -``/etc/postgresql/9.2/main/pg_hba.conf`` you have following line (you will need +``/etc/postgresql/9.3/main/pg_hba.conf`` you have following line (you will need to install an editor, e.g. ``apt-get install vim``): .. code-block:: bash host all all 0.0.0.0/0 md5 -Additionaly, inside ``/etc/postgresql/9.2/main/postgresql.conf`` +Additionaly, inside ``/etc/postgresql/9.3/main/postgresql.conf`` uncomment ``listen_addresses`` so it is as follows: .. code-block:: bash @@ -115,9 +117,9 @@ Finally, run PostgreSQL server via ``docker``. CONTAINER=$(sudo docker run -d -p 5432 \ -t /postgresql \ - /bin/su postgres -c '/usr/lib/postgresql/9.2/bin/postgres \ - -D /var/lib/postgresql/9.2/main \ - -c config_file=/etc/postgresql/9.2/main/postgresql.conf') + /bin/su postgres -c '/usr/lib/postgresql/9.3/bin/postgres \ + -D /var/lib/postgresql/9.3/main \ + -c config_file=/etc/postgresql/9.3/main/postgresql.conf') Connect the PostgreSQL server using ``psql`` (You will need postgres installed on the machine. For ubuntu, use something like @@ -132,7 +134,7 @@ As before, create roles or databases if needed. .. code-block:: bash - psql (9.2.4) + psql (9.3.1) Type "help" for help. docker=# CREATE DATABASE foo OWNER=docker; @@ -160,9 +162,9 @@ container starts. .. code-block:: bash sudo docker commit -run='{"Cmd": \ - ["/bin/su", "postgres", "-c", "/usr/lib/postgresql/9.2/bin/postgres -D \ - /var/lib/postgresql/9.2/main -c \ - config_file=/etc/postgresql/9.2/main/postgresql.conf"], "PortSpecs": ["5432"]}' \ + ["/bin/su", "postgres", "-c", "/usr/lib/postgresql/9.3/bin/postgres -D \ + /var/lib/postgresql/9.3/main -c \ + config_file=/etc/postgresql/9.3/main/postgresql.conf"], "PortSpecs": ["5432"]}' \ /postgresql From now on, just type ``docker run /postgresql`` and From 682a188ead44e93ac3d5cc53c708808fa7d2fe4f Mon Sep 17 00:00:00 2001 From: dkumor Date: Wed, 27 Nov 2013 10:25:30 -0600 Subject: [PATCH 11/23] Arch docs: Added lxc-docker-nightly AUR package, modified deps lxc-docker-nightly installs latest build. Removed go from dependencies, as it is not needed in lxc-docker and lxc-docker-nightly. The -git package will flag go as a dependency upon installation. --- docs/sources/installation/archlinux.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/sources/installation/archlinux.rst b/docs/sources/installation/archlinux.rst index e415295918..ae56badf4c 100644 --- a/docs/sources/installation/archlinux.rst +++ b/docs/sources/installation/archlinux.rst @@ -12,26 +12,28 @@ Arch Linux .. include:: install_unofficial.inc Installing on Arch Linux is not officially supported but can be handled via -either of the following AUR packages: +one of the following AUR packages: * `lxc-docker `_ * `lxc-docker-git `_ +* `lxc-docker-nightly `_ The lxc-docker package will install the latest tagged version of docker. The lxc-docker-git package will build from the current master branch. +The lxc-docker-nightly package will install the latest build. Dependencies ------------ Docker depends on several packages which are specified as dependencies in -either AUR package. +the AUR packages. The core dependencies are: * bridge-utils * device-mapper -* go * iproute2 * lxc + Installation ------------ @@ -42,7 +44,7 @@ done so before. :: - yaourt -S lxc-docker-git + yaourt -S lxc-docker Starting Docker From 2c27da881807b6c63ef29e8033241a17c76f5ab7 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Thu, 28 Nov 2013 00:39:06 +0100 Subject: [PATCH 12/23] Restore 'save' paragraph --- docs/sources/commandline/cli.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 9635675a77..e35475ae96 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -797,7 +797,7 @@ Known Issues (kill) -link="": Remove the link instead of the actual container Known Issues (rm) -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ * :issue:`197` indicates that ``docker kill`` may leave directories behind and make it difficult to remove the container. @@ -881,8 +881,15 @@ containers will not be deleted. -name="": Assign the specified name to the container. If no name is specific docker will generate a random name -P=false: Publish all exposed ports to the host interfaces -Examples --------- +Known Issues (run -volumes-from) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* :issue:`2702`: "lxc-start: Permission denied - failed to mount" + could indicate a permissions problem with AppArmor. Please see the + issue for a workaround. + +Examples: +~~~~~~~~~ .. code-block:: bash @@ -974,16 +981,10 @@ id may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in read-only or read-write mode, respectively. By default, the volumes are mounted in the same mode (rw or ro) as the reference container. -Known Issues (run -volumes-from) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* :issue:`2702`: "lxc-start: Permission denied - failed to mount" - could indicate a permissions problem with AppArmor. Please see the - issue for a workaround. - .. _cli_save: ``save`` +--------- :: From 74c8f7af756ed03131aee051b0ccb926b77e04db Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 07:16:34 +0000 Subject: [PATCH 13/23] Refactor attach loop device in pure Go --- graphdriver/devmapper/deviceset.go | 10 +- graphdriver/devmapper/devmapper.go | 125 +++++++++++++-- graphdriver/devmapper/devmapper_wrapper.go | 178 +++++---------------- graphdriver/devmapper/sys.go | 8 +- 4 files changed, 160 insertions(+), 161 deletions(-) diff --git a/graphdriver/devmapper/deviceset.go b/graphdriver/devmapper/deviceset.go index 8c2556104d..77d865ad99 100644 --- a/graphdriver/devmapper/deviceset.go +++ b/graphdriver/devmapper/deviceset.go @@ -383,7 +383,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { return fmt.Errorf("Can't shrink file") } - dataloopback := FindLoopDeviceFor(&osFile{File: datafile}) + dataloopback := FindLoopDeviceFor(datafile) if dataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", datafilename) } @@ -395,7 +395,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { } defer metadatafile.Close() - metadataloopback := FindLoopDeviceFor(&osFile{File: metadatafile}) + metadataloopback := FindLoopDeviceFor(metadatafile) if metadataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename) } @@ -491,14 +491,14 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { if info.Exists == 0 { utils.Debugf("Pool doesn't exist. Creating it.") - dataFile, err := AttachLoopDevice(data) + dataFile, err := attachLoopDevice(data) if err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } defer dataFile.Close() - metadataFile, err := AttachLoopDevice(metadata) + metadataFile, err := attachLoopDevice(metadata) if err != nil { utils.Debugf("\n--->Err: %s\n", err) return err @@ -637,7 +637,7 @@ func (devices *DeviceSet) deactivateDevice(hash string) error { // or b) the 1 second timeout expires. func (devices *DeviceSet) waitRemove(hash string) error { utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, hash) - defer utils.Debugf("[deviceset %s] waitRemove END", devices.devicePrefix, hash) + defer utils.Debugf("[deviceset %s] waitRemove(%) END", devices.devicePrefix, hash) devname, err := devices.byHash(hash) if err != nil { return err diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 89c36d45ad..305ed183d4 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dotcloud/docker/utils" "runtime" + "unsafe" ) type DevmapperLogger interface { @@ -177,15 +178,6 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, start, length, targetType, params } -func AttachLoopDevice(filename string) (*osFile, error) { - var fd int - res := DmAttachLoopDevice(filename, &fd) - if res == "" { - return nil, ErrAttachLoopbackDevice - } - return &osFile{File: osNewFile(uintptr(fd), res)}, nil -} - func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) { dev, inode, err := DmGetLoopbackBackingFile(file.Fd()) if err != 0 { @@ -223,11 +215,10 @@ func FindLoopDeviceFor(file *osFile) *osFile { continue } - dev, inode, err := getLoopbackBackingFile(&osFile{File: file}) + dev, inode, err := getLoopbackBackingFile(file) if err == nil && dev == targetDevice && inode == targetInode { - return &osFile{File: file} + return file } - file.Close() } @@ -420,7 +411,7 @@ func suspendDevice(name string) error { return err } if err := task.Run(); err != nil { - return fmt.Errorf("Error running DeviceSuspend") + return fmt.Errorf("Error running DeviceSuspend: %s", err) } return nil } @@ -437,7 +428,7 @@ func resumeDevice(name string) error { } if err := task.Run(); err != nil { - return fmt.Errorf("Error running DeviceSuspend") + return fmt.Errorf("Error running DeviceResume") } UdevWait(cookie) @@ -574,3 +565,109 @@ func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseNa return nil } + +type LoopInfo64 struct { + loDevice uint64 /* ioctl r/o */ + loInode uint64 /* ioctl r/o */ + loRdevice uint64 /* ioctl r/o */ + loOffset uint64 + loSizelimit uint64 /* bytes, 0 == max available */ + loNumber uint32 /* ioctl r/o */ + loEncrypt_type uint32 + loEncrypt_key_size uint32 /* ioctl w/o */ + loFlags uint32 /* ioctl r/o */ + loFileName [LoNameSize]uint8 + loCryptName [LoNameSize]uint8 + loEncryptKey [LoKeySize]uint8 /* ioctl w/o */ + loInit [2]uint64 +} + +// attachLoopDevice attaches the given sparse file to the next +// available loopback device. It returns an opened *osFile. +func attachLoopDevice(filename string) (loop *osFile, err error) { + startIndex := 0 + + // Try to retrieve the next available loopback device via syscall. + // If it fails, we discard error and start loopking for a + // loopback from index 0. + if f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644); err == nil { + if index, _, err := sysSyscall(sysSysIoctl, f.Fd(), LoopCtlGetFree, 0); err != 0 { + utils.Debugf("Error retrieving the next available loopback: %s", err) + } else if index > 0 { + startIndex = int(index) + } + f.Close() + } + + // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) + f, err := osOpenFile(filename, osORdWr, 0644) + if err != nil { + return nil, err + } + defer f.Close() + + var ( + target string + loopFile *osFile + ) + // Start looking for a free /dev/loop + for i := startIndex; ; { + target = fmt.Sprintf("/dev/loop%d", i) + + fi, err := osStat(target) + if err != nil { + if osIsNotExist(err) { + utils.Errorf("There are no more loopback device available.") + } + } + + // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) + if fi.IsDir() { + } + + // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) + loopFile, err = osOpenFile(target, osORdWr, 0644) + if err != nil { + return nil, err + } + + // Try to attach to the loop file + if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopSetFd, f.Fd()); err != 0 { + loopFile.Close() + // If the error is EBUSY, then try the next loopback + if err != sysEBusy { + utils.Errorf("Cannot set up loopback device %s: %s", target, err) + return nil, err + } + } else { + // In case of success, we finished. Break the loop. + break + } + // In case of EBUSY error, the loop keep going. + } + + // This can't happen, but let's be sure + if loopFile == nil { + return nil, fmt.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", filename) + } + + // Set the status of the loopback device + var loopInfo LoopInfo64 + + // Due to type incompatibility (string vs [64]uint8), we copy data + copy(loopInfo.loFileName[:], target[:]) + loopInfo.loOffset = 0 + loopInfo.loFlags = LoFlagsAutoClear + + if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopSetStatus64, uintptr(unsafe.Pointer(&loopInfo))); err != 0 { + // If the call failed, then free the loopback device + utils.Errorf("Cannot set up loopback device info: %s", err) + if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopClrFd, 0); err != 0 { + utils.Errorf("Error while cleaning up the loopback device") + } + loopFile.Close() + return nil, err + } + + return loopFile, nil +} diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 818c968bbf..0ed5bcd332 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -2,124 +2,14 @@ package devmapper /* #cgo LDFLAGS: -L. -ldevmapper -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#ifndef LOOP_CTL_GET_FREE -#define LOOP_CTL_GET_FREE 0x4C82 -#endif - -// FIXME: this could easily be rewritten in go -char* attach_loop_device(const char *filename, int *loop_fd_out) -{ - struct loop_info64 loopinfo = {0}; - struct stat st; - char buf[64]; - int i, loop_fd, fd, start_index; - char* loopname; - - - *loop_fd_out = -1; - - start_index = 0; - fd = open("/dev/loop-control", O_RDONLY); - if (fd >= 0) { - start_index = ioctl(fd, LOOP_CTL_GET_FREE); - close(fd); - - if (start_index < 0) - start_index = 0; - } - - fd = open(filename, O_RDWR); - if (fd < 0) { - perror("open"); - return NULL; - } - - loop_fd = -1; - for (i = start_index ; loop_fd < 0 ; i++ ) { - if (sprintf(buf, "/dev/loop%d", i) < 0) { - close(fd); - return NULL; - } - - if (stat(buf, &st)) { - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf); - } else if (errno == ENOENT) { - fprintf(stderr, "[error] There are no more loopback device available.\n"); - } else { - fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno); - } - close(fd); - return NULL; - } - - loop_fd = open(buf, O_RDWR); - if (loop_fd < 0 && errno == ENOENT) { - fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf); - close(fd); - return NULL; - } else if (loop_fd < 0) { - fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno); - continue; - } - - if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) { - int errsv = errno; - close(loop_fd); - loop_fd = -1; - if (errsv != EBUSY) { - close(fd); - fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv)); - return NULL; - } - continue; - } - - close(fd); - - strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE); - loopinfo.lo_offset = 0; - loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR; - - if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) { - perror("ioctl LOOP_SET_STATUS64"); - if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { - perror("ioctl LOOP_CLR_FD"); - } - close(loop_fd); - fprintf (stderr, "cannot set up loopback device info"); - return (NULL); - } - - loopname = strdup(buf); - if (loopname == NULL) { - close(loop_fd); - return (NULL); - } - - *loop_fd_out = loop_fd; - return (loopname); - } - - return (NULL); -} +#include // FIXME: present only for defines, maybe we can remove it? +#include // FIXME: present only for BLKGETSIZE64, maybe we can remove it? +// FIXME: Can't we find a way to do the logging in pure Go? extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); -static void log_cb(int level, const char *file, int line, - int dm_errno_or_class, const char *f, ...) +static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) { char buffer[256]; va_list ap; @@ -135,7 +25,6 @@ static void log_with_errno_init() { dm_log_with_errno_init(log_cb); } - */ import "C" @@ -147,6 +36,19 @@ type ( CDmTask C.struct_dm_task ) +// FIXME: Make sure the values are defined in C +const ( + LoopSetFd = C.LOOP_SET_FD + LoopCtlGetFree = C.LOOP_CTL_GET_FREE + LoopSetStatus64 = C.LOOP_SET_STATUS64 + LoopClrFd = C.LOOP_CLR_FD + LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR + LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY + LoFlagsPartScan = C.LO_FLAGS_PARTSCAN + LoKeySize = C.LO_KEY_SIZE + LoNameSize = C.LO_NAME_SIZE +) + var ( DmAttachLoopDevice = dmAttachLoopDeviceFct DmGetBlockSize = dmGetBlockSizeFct @@ -185,28 +87,26 @@ func dmTaskCreateFct(taskType int) *CDmTask { } func dmTaskRunFct(task *CDmTask) int { - return int(C.dm_task_run((*C.struct_dm_task)(task))) + ret, _ := C.dm_task_run((*C.struct_dm_task)(task)) + return int(ret) } func dmTaskSetNameFct(task *CDmTask, name string) int { Cname := C.CString(name) defer free(Cname) - return int(C.dm_task_set_name((*C.struct_dm_task)(task), - Cname)) + return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname)) } func dmTaskSetMessageFct(task *CDmTask, message string) int { Cmessage := C.CString(message) defer free(Cmessage) - return int(C.dm_task_set_message((*C.struct_dm_task)(task), - Cmessage)) + return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage)) } func dmTaskSetSectorFct(task *CDmTask, sector uint64) int { - return int(C.dm_task_set_sector((*C.struct_dm_task)(task), - C.uint64_t(sector))) + return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector))) } func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int { @@ -214,13 +114,11 @@ func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int { defer func() { *cookie = uint(cCookie) }() - return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, - C.uint16_t(flags))) + return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags))) } func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int { - return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), - C.dm_add_node_t(addNode))) + return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) } func dmTaskSetRoFct(task *CDmTask) int { @@ -236,14 +134,12 @@ func dmTaskAddTargetFct(task *CDmTask, Cparams := C.CString(params) defer free(Cparams) - return int(C.dm_task_add_target((*C.struct_dm_task)(task), - C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) + return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) } func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { var lo64 C.struct_loop_info64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, - uintptr(unsafe.Pointer(&lo64))) + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) } @@ -287,23 +183,23 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, targ *params = C.GoString(Cparams) }() - nextp := C.dm_get_next_target((*C.struct_dm_task)(task), - unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) + nextp := C.dm_get_next_target((*C.struct_dm_task)(task), unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) return uintptr(nextp) } func dmAttachLoopDeviceFct(filename string, fd *int) string { - cFilename := C.CString(filename) - defer free(cFilename) + return "" + // cFilename := C.CString(filename) + // defer free(cFilename) - var cFd C.int - defer func() { - *fd = int(cFd) - }() + // var cFd C.int + // defer func() { + // *fd = int(cFd) + // }() - ret := C.attach_loop_device(cFilename, &cFd) - defer free(ret) - return C.GoString(ret) + // ret := C.attach_loop_device(cFilename, &cFd) + // defer free(ret) + // return C.GoString(ret) } func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go index 60bafb5f6d..152e68285b 100644 --- a/graphdriver/devmapper/sys.go +++ b/graphdriver/devmapper/sys.go @@ -19,7 +19,11 @@ var ( sysCloseOnExec = syscall.CloseOnExec sysSyscall = syscall.Syscall - osOpenFile = os.OpenFile + osOpenFile = func(name string, flag int, perm os.FileMode) (*osFile, error) { + f, err := os.OpenFile(name, flag, perm) + return &osFile{File: f}, err + } + osOpen = func(name string) (*osFile, error) { f, err := os.Open(name); return &osFile{File: f}, err } osNewFile = os.NewFile osCreate = os.Create osStat = os.Stat @@ -40,7 +44,9 @@ const ( sysMsRdOnly = syscall.MS_RDONLY sysEInval = syscall.EINVAL sysSysIoctl = syscall.SYS_IOCTL + sysEBusy = syscall.EBUSY + osORdOnly = os.O_RDONLY osORdWr = os.O_RDWR osOCreate = os.O_CREATE ) From eb528b959efc0a3924939991be45704047fd5aea Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 17:12:57 -0800 Subject: [PATCH 14/23] Move attach loop device to its own file --- graphdriver/devmapper/attachLoopback.go | 178 +++++++++++++++++++++ graphdriver/devmapper/devmapper.go | 107 ------------- graphdriver/devmapper/devmapper_wrapper.go | 60 +++---- 3 files changed, 208 insertions(+), 137 deletions(-) create mode 100644 graphdriver/devmapper/attachLoopback.go diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attachLoopback.go new file mode 100644 index 0000000000..d81a482ac4 --- /dev/null +++ b/graphdriver/devmapper/attachLoopback.go @@ -0,0 +1,178 @@ +package devmapper + +import ( + "fmt" + "github.com/dotcloud/docker/utils" + "unsafe" +) + +func ioctlLoopCtlGetFree(fd uintptr) (int, error) { + index, _, err := sysSyscall(sysSysIoctl, fd, LoopCtlGetFree, 0) + if err != 0 { + return 0, err + } + return int(index), nil +} + +func ioctlLoopSetFd(loopFd, sparseFd uintptr) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetFd, sparseFd); err != 0 { + return err + } + return nil +} + +func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error { + _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))) + if err != 0 { + return err + } + return nil +} + +func ioctlLoopClrFd(loopFd uintptr) error { + _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0) + if err != 0 { + return err + } + return nil +} + +// //func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { +// func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) { +// var lo64 C.struct_loop_info64 +// _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) +// return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) +// } + +// func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { +// _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) +// return sysErrno(err) +// } + +// func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { +// var size int64 +// _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) +// return size, sysErrno(err) +// } + +// func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { +// _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) +// return sysErrno(err) +// } + +func getNextFreeLoopbackIndex() (int, error) { + f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644) + if err != nil { + return 0, err + } + defer f.Close() + + index, err := ioctlLoopCtlGetFree(f.Fd()) + if index < 0 { + index = 0 + } + return index, err +} + +func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, err error) { + // Start looking for a free /dev/loop + for { + target := fmt.Sprintf("/dev/loop%d", index) + index++ + + fi, err := osStat(target) + if err != nil { + if osIsNotExist(err) { + utils.Errorf("There are no more loopback device available.") + } + return nil, ErrAttachLoopbackDevice + } + + // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) + if fi.IsDir() { + } + + // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) + loopFile, err = osOpenFile(target, osORdWr, 0644) + if err != nil { + utils.Errorf("Error openning loopback device: %s", err) + return nil, ErrAttachLoopbackDevice + } + + // Try to attach to the loop file + if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil { + loopFile.Close() + + // If the error is EBUSY, then try the next loopback + if err != sysEBusy { + utils.Errorf("Cannot set up loopback device %s: %s", target, err) + return nil, ErrAttachLoopbackDevice + } + + // Otherwise, we keep going with the loop + continue + } + // In case of success, we finished. Break the loop. + break + } + + // This can't happen, but let's be sure + if loopFile == nil { + utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name()) + return nil, ErrAttachLoopbackDevice + } + + return loopFile, nil +} + +func stringToLoopName(src string) [LoNameSize]uint8 { + var dst [LoNameSize]uint8 + copy(dst[:], src[:]) + return dst +} + +// attachLoopDevice attaches the given sparse file to the next +// available loopback device. It returns an opened *osFile. +func attachLoopDevice(sparseName string) (loop *osFile, err error) { + + // Try to retrieve the next available loopback device via syscall. + // If it fails, we discard error and start loopking for a + // loopback from index 0. + startIndex, err := getNextFreeLoopbackIndex() + if err != nil { + utils.Debugf("Error retrieving the next available loopback: %s", err) + } + + // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) + sparseFile, err := osOpenFile(sparseName, osORdWr, 0644) + if err != nil { + utils.Errorf("Error openning sparse file %s: %s", sparseName, err) + return nil, ErrAttachLoopbackDevice + } + defer sparseFile.Close() + + loopFile, err := openNextAvailableLoopback(startIndex, sparseFile) + if err != nil { + return nil, err + } + + // Set the status of the loopback device + loopInfo := &LoopInfo64{ + loFileName: stringToLoopName(loopFile.Name()), + loOffset: 0, + loFlags: LoFlagsAutoClear, + } + + if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil { + utils.Errorf("Cannot set up loopback device info: %s", err) + + // If the call failed, then free the loopback device + if err := ioctlLoopClrFd(loopFile.Fd()); err != nil { + utils.Errorf("Error while cleaning up the loopback device") + } + loopFile.Close() + return nil, ErrAttachLoopbackDevice + } + + return loopFile, nil +} diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 305ed183d4..93faf5018d 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dotcloud/docker/utils" "runtime" - "unsafe" ) type DevmapperLogger interface { @@ -565,109 +564,3 @@ func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseNa return nil } - -type LoopInfo64 struct { - loDevice uint64 /* ioctl r/o */ - loInode uint64 /* ioctl r/o */ - loRdevice uint64 /* ioctl r/o */ - loOffset uint64 - loSizelimit uint64 /* bytes, 0 == max available */ - loNumber uint32 /* ioctl r/o */ - loEncrypt_type uint32 - loEncrypt_key_size uint32 /* ioctl w/o */ - loFlags uint32 /* ioctl r/o */ - loFileName [LoNameSize]uint8 - loCryptName [LoNameSize]uint8 - loEncryptKey [LoKeySize]uint8 /* ioctl w/o */ - loInit [2]uint64 -} - -// attachLoopDevice attaches the given sparse file to the next -// available loopback device. It returns an opened *osFile. -func attachLoopDevice(filename string) (loop *osFile, err error) { - startIndex := 0 - - // Try to retrieve the next available loopback device via syscall. - // If it fails, we discard error and start loopking for a - // loopback from index 0. - if f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644); err == nil { - if index, _, err := sysSyscall(sysSysIoctl, f.Fd(), LoopCtlGetFree, 0); err != 0 { - utils.Debugf("Error retrieving the next available loopback: %s", err) - } else if index > 0 { - startIndex = int(index) - } - f.Close() - } - - // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) - f, err := osOpenFile(filename, osORdWr, 0644) - if err != nil { - return nil, err - } - defer f.Close() - - var ( - target string - loopFile *osFile - ) - // Start looking for a free /dev/loop - for i := startIndex; ; { - target = fmt.Sprintf("/dev/loop%d", i) - - fi, err := osStat(target) - if err != nil { - if osIsNotExist(err) { - utils.Errorf("There are no more loopback device available.") - } - } - - // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) - if fi.IsDir() { - } - - // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) - loopFile, err = osOpenFile(target, osORdWr, 0644) - if err != nil { - return nil, err - } - - // Try to attach to the loop file - if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopSetFd, f.Fd()); err != 0 { - loopFile.Close() - // If the error is EBUSY, then try the next loopback - if err != sysEBusy { - utils.Errorf("Cannot set up loopback device %s: %s", target, err) - return nil, err - } - } else { - // In case of success, we finished. Break the loop. - break - } - // In case of EBUSY error, the loop keep going. - } - - // This can't happen, but let's be sure - if loopFile == nil { - return nil, fmt.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", filename) - } - - // Set the status of the loopback device - var loopInfo LoopInfo64 - - // Due to type incompatibility (string vs [64]uint8), we copy data - copy(loopInfo.loFileName[:], target[:]) - loopInfo.loOffset = 0 - loopInfo.loFlags = LoFlagsAutoClear - - if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopSetStatus64, uintptr(unsafe.Pointer(&loopInfo))); err != 0 { - // If the call failed, then free the loopback device - utils.Errorf("Cannot set up loopback device info: %s", err) - if _, _, err := sysSyscall(sysSysIoctl, loopFile.Fd(), LoopClrFd, 0); err != 0 { - utils.Errorf("Error while cleaning up the loopback device") - } - loopFile.Close() - return nil, err - } - - return loopFile, nil -} diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 0ed5bcd332..24d9502b49 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -33,7 +33,23 @@ import ( ) type ( - CDmTask C.struct_dm_task + CDmTask C.struct_dm_task + CLoopInfo64 C.struct_loop_info64 + LoopInfo64 struct { + loDevice uint64 /* ioctl r/o */ + loInode uint64 /* ioctl r/o */ + loRdevice uint64 /* ioctl r/o */ + loOffset uint64 + loSizelimit uint64 /* bytes, 0 == max available */ + loNumber uint32 /* ioctl r/o */ + loEncrypt_type uint32 + loEncrypt_key_size uint32 /* ioctl w/o */ + loFlags uint32 /* ioctl r/o */ + loFileName [LoNameSize]uint8 + loCryptName [LoNameSize]uint8 + loEncryptKey [LoKeySize]uint8 /* ioctl w/o */ + loInit [2]uint64 + } ) // FIXME: Make sure the values are defined in C @@ -50,7 +66,6 @@ const ( ) var ( - DmAttachLoopDevice = dmAttachLoopDeviceFct DmGetBlockSize = dmGetBlockSizeFct DmGetLibraryVersion = dmGetLibraryVersionFct DmGetNextTarget = dmGetNextTargetFct @@ -137,23 +152,6 @@ func dmTaskAddTargetFct(task *CDmTask, return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) } -func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { - var lo64 C.struct_loop_info64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) - return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) -} - -func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) - return sysErrno(err) -} - -func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { - var size int64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - return size, sysErrno(err) -} - func dmTaskGetInfoFct(task *CDmTask, info *Info) int { Cinfo := C.struct_dm_info{} defer func() { @@ -187,19 +185,21 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, targ return uintptr(nextp) } -func dmAttachLoopDeviceFct(filename string, fd *int) string { - return "" - // cFilename := C.CString(filename) - // defer free(cFilename) +func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { + var lo64 C.struct_loop_info64 + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) + return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) +} - // var cFd C.int - // defer func() { - // *fd = int(cFd) - // }() +func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) + return sysErrno(err) +} - // ret := C.attach_loop_device(cFilename, &cFd) - // defer free(ret) - // return C.GoString(ret) +func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { + var size int64 + _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) + return size, sysErrno(err) } func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { From 1214b8897bba2a33a7ded8779bcdc966fe1cb176 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 17:44:54 -0800 Subject: [PATCH 15/23] Extract ioctl from wrapper --- graphdriver/devmapper/attachLoopback.go | 63 ++---------------- graphdriver/devmapper/devmapper.go | 15 +++-- graphdriver/devmapper/devmapper_wrapper.go | 76 ++++++++-------------- graphdriver/devmapper/ioctl.go | 58 +++++++++++++++++ 4 files changed, 99 insertions(+), 113 deletions(-) create mode 100644 graphdriver/devmapper/ioctl.go diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attachLoopback.go index d81a482ac4..98d0bf5f5b 100644 --- a/graphdriver/devmapper/attachLoopback.go +++ b/graphdriver/devmapper/attachLoopback.go @@ -3,63 +3,14 @@ package devmapper import ( "fmt" "github.com/dotcloud/docker/utils" - "unsafe" ) -func ioctlLoopCtlGetFree(fd uintptr) (int, error) { - index, _, err := sysSyscall(sysSysIoctl, fd, LoopCtlGetFree, 0) - if err != 0 { - return 0, err - } - return int(index), nil +func stringToLoopName(src string) [LoNameSize]uint8 { + var dst [LoNameSize]uint8 + copy(dst[:], src[:]) + return dst } -func ioctlLoopSetFd(loopFd, sparseFd uintptr) error { - if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetFd, sparseFd); err != 0 { - return err - } - return nil -} - -func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error { - _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))) - if err != 0 { - return err - } - return nil -} - -func ioctlLoopClrFd(loopFd uintptr) error { - _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0) - if err != 0 { - return err - } - return nil -} - -// //func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { -// func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) { -// var lo64 C.struct_loop_info64 -// _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) -// return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) -// } - -// func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { -// _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) -// return sysErrno(err) -// } - -// func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { -// var size int64 -// _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) -// return size, sysErrno(err) -// } - -// func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { -// _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) -// return sysErrno(err) -// } - func getNextFreeLoopbackIndex() (int, error) { f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644) if err != nil { @@ -125,12 +76,6 @@ func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, return loopFile, nil } -func stringToLoopName(src string) [LoNameSize]uint8 { - var dst [LoNameSize]uint8 - copy(dst[:], src[:]) - return dst -} - // attachLoopDevice attaches the given sparse file to the next // available loopback device. It returns an opened *osFile. func attachLoopDevice(sparseName string) (loop *osFile, err error) { diff --git a/graphdriver/devmapper/devmapper.go b/graphdriver/devmapper/devmapper.go index 93faf5018d..db41ed6c25 100644 --- a/graphdriver/devmapper/devmapper.go +++ b/graphdriver/devmapper/devmapper.go @@ -178,15 +178,17 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, } func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) { - dev, inode, err := DmGetLoopbackBackingFile(file.Fd()) - if err != 0 { + loopInfo, err := ioctlLoopGetStatus64(file.Fd()) + if err != nil { + utils.Errorf("Error get loopback backing file: %s\n", err) return 0, 0, ErrGetLoopbackBackingFile } - return dev, inode, nil + return loopInfo.loDevice, loopInfo.loInode, nil } func LoopbackSetCapacity(file *osFile) error { - if err := DmLoopbackSetCapacity(file.Fd()); err != 0 { + if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil { + utils.Errorf("Error loopbackSetCapacity: %s", err) return ErrLoopbackSetCapacity } return nil @@ -276,8 +278,9 @@ func RemoveDevice(name string) error { } func GetBlockDeviceSize(file *osFile) (uint64, error) { - size, errno := DmGetBlockSize(file.Fd()) - if size == -1 || errno != 0 { + size, err := ioctlBlkGetSize64(file.Fd()) + if err != nil { + utils.Errorf("Error getblockdevicesize: %s", err) return 0, ErrGetBlockSize } return uint64(size), nil diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 24d9502b49..1b636d392f 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -33,7 +33,8 @@ import ( ) type ( - CDmTask C.struct_dm_task + CDmTask C.struct_dm_task + CLoopInfo64 C.struct_loop_info64 LoopInfo64 struct { loDevice uint64 /* ioctl r/o */ @@ -54,39 +55,40 @@ type ( // FIXME: Make sure the values are defined in C const ( - LoopSetFd = C.LOOP_SET_FD - LoopCtlGetFree = C.LOOP_CTL_GET_FREE - LoopSetStatus64 = C.LOOP_SET_STATUS64 - LoopClrFd = C.LOOP_CLR_FD + LoopSetFd = C.LOOP_SET_FD + LoopCtlGetFree = C.LOOP_CTL_GET_FREE + LoopGetStatus64 = C.LOOP_GET_STATUS64 + LoopSetStatus64 = C.LOOP_SET_STATUS64 + LoopClrFd = C.LOOP_CLR_FD + LoopSetCapacity = C.LOOP_SET_CAPACITY + LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY LoFlagsPartScan = C.LO_FLAGS_PARTSCAN LoKeySize = C.LO_KEY_SIZE LoNameSize = C.LO_NAME_SIZE + + BlkGetSize64 = C.BLKGETSIZE64 ) var ( - DmGetBlockSize = dmGetBlockSizeFct - DmGetLibraryVersion = dmGetLibraryVersionFct - DmGetNextTarget = dmGetNextTargetFct - DmLogInitVerbose = dmLogInitVerboseFct - DmSetDevDir = dmSetDevDirFct - DmTaskAddTarget = dmTaskAddTargetFct - DmTaskCreate = dmTaskCreateFct - DmTaskDestroy = dmTaskDestroyFct - DmTaskGetInfo = dmTaskGetInfoFct - DmTaskRun = dmTaskRunFct - DmTaskSetAddNode = dmTaskSetAddNodeFct - DmTaskSetCookie = dmTaskSetCookieFct - DmTaskSetMessage = dmTaskSetMessageFct - DmTaskSetName = dmTaskSetNameFct - DmTaskSetRo = dmTaskSetRoFct - DmTaskSetSector = dmTaskSetSectorFct - DmUdevWait = dmUdevWaitFct - GetBlockSize = getBlockSizeFct - LogWithErrnoInit = logWithErrnoInitFct - DmGetLoopbackBackingFile = dmGetLoopbackBackingFileFct - DmLoopbackSetCapacity = dmLoopbackSetCapacityFct + DmGetLibraryVersion = dmGetLibraryVersionFct + DmGetNextTarget = dmGetNextTargetFct + DmLogInitVerbose = dmLogInitVerboseFct + DmSetDevDir = dmSetDevDirFct + DmTaskAddTarget = dmTaskAddTargetFct + DmTaskCreate = dmTaskCreateFct + DmTaskDestroy = dmTaskDestroyFct + DmTaskGetInfo = dmTaskGetInfoFct + DmTaskRun = dmTaskRunFct + DmTaskSetAddNode = dmTaskSetAddNodeFct + DmTaskSetCookie = dmTaskSetCookieFct + DmTaskSetMessage = dmTaskSetMessageFct + DmTaskSetName = dmTaskSetNameFct + DmTaskSetRo = dmTaskSetRoFct + DmTaskSetSector = dmTaskSetSectorFct + DmUdevWait = dmUdevWaitFct + LogWithErrnoInit = logWithErrnoInitFct ) func free(p *C.char) { @@ -185,28 +187,6 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, targ return uintptr(nextp) } -func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) { - var lo64 C.struct_loop_info64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) - return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) -} - -func dmLoopbackSetCapacityFct(fd uintptr) sysErrno { - _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) - return sysErrno(err) -} - -func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { - var size int64 - _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - return size, sysErrno(err) -} - -func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { - _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) - return sysErrno(err) -} - func dmUdevWaitFct(cookie uint) int { return int(C.dm_udev_wait(C.uint32_t(cookie))) } diff --git a/graphdriver/devmapper/ioctl.go b/graphdriver/devmapper/ioctl.go new file mode 100644 index 0000000000..65c6c1a512 --- /dev/null +++ b/graphdriver/devmapper/ioctl.go @@ -0,0 +1,58 @@ +package devmapper + +import ( + "unsafe" +) + +func ioctlLoopCtlGetFree(fd uintptr) (int, error) { + index, _, err := sysSyscall(sysSysIoctl, fd, LoopCtlGetFree, 0) + if err != 0 { + return 0, err + } + return int(index), nil +} + +func ioctlLoopSetFd(loopFd, sparseFd uintptr) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetFd, sparseFd); err != 0 { + return err + } + return nil +} + +func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { + return err + } + return nil +} + +func ioctlLoopClrFd(loopFd uintptr) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0); err != 0 { + return err + } + return nil +} + +func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) { + loopInfo := &LoopInfo64{} + + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { + return nil, err + } + return loopInfo, nil +} + +func ioctlLoopSetCapacity(loopFd uintptr, value int) error { + if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetCapacity, uintptr(value)); err != 0 { + return err + } + return nil +} + +func ioctlBlkGetSize64(fd uintptr) (int64, error) { + var size int64 + if _, _, err := sysSyscall(sysSysIoctl, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 { + return 0, err + } + return size, nil +} From 8a5d927a5337b1c9a0c8478c4e522ce0ded09b85 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 27 Nov 2013 18:21:17 -0800 Subject: [PATCH 16/23] Check if the target loopback is a block device --- graphdriver/devmapper/attachLoopback.go | 5 +++-- graphdriver/devmapper/sys.go | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attachLoopback.go index 98d0bf5f5b..0d556d81c2 100644 --- a/graphdriver/devmapper/attachLoopback.go +++ b/graphdriver/devmapper/attachLoopback.go @@ -39,8 +39,9 @@ func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, return nil, ErrAttachLoopbackDevice } - // FIXME: Check here if target is a block device (in C: S_ISBLK(mode)) - if fi.IsDir() { + if fi.Mode()&osModeDevice != osModeDevice { + utils.Errorf("Loopback device %s is not a block device.", target) + continue } // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go index 152e68285b..9cf124a9a5 100644 --- a/graphdriver/devmapper/sys.go +++ b/graphdriver/devmapper/sys.go @@ -46,9 +46,10 @@ const ( sysSysIoctl = syscall.SYS_IOCTL sysEBusy = syscall.EBUSY - osORdOnly = os.O_RDONLY - osORdWr = os.O_RDWR - osOCreate = os.O_CREATE + osORdOnly = os.O_RDONLY + osORdWr = os.O_RDWR + osOCreate = os.O_CREATE + osModeDevice = os.ModeDevice ) func toSysStatT(i interface{}) *sysStatT { From c226ab6d9edb6b74547348c309c633f207bd4aa4 Mon Sep 17 00:00:00 2001 From: Ulysse Carion Date: Wed, 23 Oct 2013 09:19:15 -0700 Subject: [PATCH 17/23] Document setting up Vagrant-docker with the remote API --- contrib/vagrant-docker/README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/contrib/vagrant-docker/README.md b/contrib/vagrant-docker/README.md index fa28e448eb..836f503059 100644 --- a/contrib/vagrant-docker/README.md +++ b/contrib/vagrant-docker/README.md @@ -17,3 +17,34 @@ meaning you can use Vagrant to control Docker containers. * [docker-provider](https://github.com/fgrehm/docker-provider) * [vagrant-shell](https://github.com/destructuring/vagrant-shell) + +## Setting up Vagrant-docker with the Remote API + +The initial Docker upstart script will not work because it runs on `127.0.0.1`, which is not accessible to the host machine. Instead, we need to change the script to connect to `0.0.0.0`. To do this, modify `/etc/init/docker.conf` to look like this: + +``` +description "Docker daemon" + +start on filesystem and started lxc-net +stop on runlevel [!2345] + +respawn + +script + /usr/bin/docker -d -H=tcp://0.0.0.0:4243/ +end script +``` + +Once that's done, you need to set up a SSH tunnel between your host machine and the vagrant machine that's running Docker. This can be done by running the following command in a host terminal: + +``` +ssh -L 4243:localhost:4243 -p 2222 vagrant@localhost +``` + +(The first 4243 is what your host can connect to, the second 4243 is what port Docker is running on in the vagrant machine, and the 2222 is the port Vagrant is providing for SSH. If VirtualBox is the VM you're using, you can see what value "2222" should be by going to: Network > Adapter 1 > Advanced > Port Forwarding in the VirtualBox GUI.) + +Note that because the port has been changed, to run docker commands from within the command line you must run them like this: + +``` +sudo docker -H 0.0.0.0:4243 < commands for docker > +``` From f1e44e0b0c6eff011c596008c75f7fdff09dbf5e Mon Sep 17 00:00:00 2001 From: John Warwick Date: Thu, 28 Nov 2013 09:55:15 -0500 Subject: [PATCH 18/23] Remove explanation of removed argument --- docs/sources/examples/hello_world.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/sources/examples/hello_world.rst b/docs/sources/examples/hello_world.rst index 99eaa2c483..c25070fcd8 100644 --- a/docs/sources/examples/hello_world.rst +++ b/docs/sources/examples/hello_world.rst @@ -131,8 +131,6 @@ Attach to the container to see the results in real-time. - **"docker attach**" This will allow us to attach to a background process to see what is going on. -- **"-sig-proxy=true"** Proxify all received signal to the process - (even in non-tty mode) - **$CONTAINER_ID** The Id of the container we want to attach too. Exit from the container attachment by pressing Control-C. From d47507791e14908e78cf38d415a9863c9ef75c5e Mon Sep 17 00:00:00 2001 From: Danny Yates Date: Thu, 28 Nov 2013 16:42:29 +0000 Subject: [PATCH 19/23] Stop invalid calls to Registry This code was resulting in a call for /v1/images///ancestry which the Registry doesn't understand. Furthermore, it was masking the original error. --- server.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/server.go b/server.go index 3641e2fdc8..b4635c8444 100644 --- a/server.go +++ b/server.go @@ -1011,16 +1011,9 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut localName = remoteName } - err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel) - if err == registry.ErrLoginRequired { + if err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel); err != nil { return err } - if err != nil { - if err := srv.pullImage(r, out, remoteName, endpoint, nil, sf); err != nil { - return err - } - return nil - } return nil } From 533067bba47acec0f65dc7e499944b9402bafb04 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 28 Nov 2013 10:37:03 -0800 Subject: [PATCH 20/23] Rename file for consistency --- graphdriver/devmapper/{attachLoopback.go => attach_loopback.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename graphdriver/devmapper/{attachLoopback.go => attach_loopback.go} (100%) diff --git a/graphdriver/devmapper/attachLoopback.go b/graphdriver/devmapper/attach_loopback.go similarity index 100% rename from graphdriver/devmapper/attachLoopback.go rename to graphdriver/devmapper/attach_loopback.go From 9f46779d42c9b90a70c3c434d03a4502070e1b6d Mon Sep 17 00:00:00 2001 From: Paul Nasrat Date: Tue, 19 Nov 2013 14:25:17 -0500 Subject: [PATCH 21/23] Wire in pprof handlers. Based on http://stackoverflow.com/questions/19591065/profiling-go-web-application-built-with-gorillas-mux-with-net-http-pprof --- api.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api.go b/api.go index 2880d0e8bc..aadd79e3c8 100644 --- a/api.go +++ b/api.go @@ -15,6 +15,7 @@ import ( "mime" "net" "net/http" + "net/http/pprof" "os" "os/exec" "regexp" @@ -1037,9 +1038,21 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s } } +func AttachProfiler(router *mux.Router) { + router.HandleFunc("/debug/pprof/", pprof.Index) + router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + router.HandleFunc("/debug/pprof/profile", pprof.Profile) + router.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP) + router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP) + router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP) +} + func createRouter(srv *Server, logging bool) (*mux.Router, error) { r := mux.NewRouter() - + if os.Getenv("DEBUG") != "" { + AttachProfiler(r) + } m := map[string]map[string]HttpApiFunc{ "GET": { "/events": getEvents, From a990b3aeb9640d35e73f99030e03e58e71924ed5 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 28 Nov 2013 11:02:53 -0800 Subject: [PATCH 22/23] Correct comments --- graphdriver/devmapper/attach_loopback.go | 4 ++-- graphdriver/devmapper/sys.go | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/graphdriver/devmapper/attach_loopback.go b/graphdriver/devmapper/attach_loopback.go index 0d556d81c2..f91bd20165 100644 --- a/graphdriver/devmapper/attach_loopback.go +++ b/graphdriver/devmapper/attach_loopback.go @@ -44,7 +44,7 @@ func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, continue } - // Open the targeted loopback (use OpenFile because Open sets O_CLOEXEC) + // OpenFile adds O_CLOEXEC loopFile, err = osOpenFile(target, osORdWr, 0644) if err != nil { utils.Errorf("Error openning loopback device: %s", err) @@ -89,7 +89,7 @@ func attachLoopDevice(sparseName string) (loop *osFile, err error) { utils.Debugf("Error retrieving the next available loopback: %s", err) } - // Open the given sparse file (use OpenFile because Open sets O_CLOEXEC) + // OpenFile adds O_CLOEXEC sparseFile, err := osOpenFile(sparseName, osORdWr, 0644) if err != nil { utils.Errorf("Error openning sparse file %s: %s", sparseName, err) diff --git a/graphdriver/devmapper/sys.go b/graphdriver/devmapper/sys.go index 9cf124a9a5..e06165c824 100644 --- a/graphdriver/devmapper/sys.go +++ b/graphdriver/devmapper/sys.go @@ -34,9 +34,7 @@ var ( osRename = os.Rename osReadlink = os.Readlink - execRun = func(name string, args ...string) error { - return exec.Command(name, args...).Run() - } + execRun = func(name string, args ...string) error { return exec.Command(name, args...).Run() } ) const ( From 261bd0d187cbc8f650d77e5c93872332c290b164 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 28 Nov 2013 11:53:09 -0800 Subject: [PATCH 23/23] Improve devmapper unit tests with syscall/ioctl --- graphdriver/devmapper/devmapper_wrapper.go | 7 +- graphdriver/devmapper/driver_test.go | 124 +++++++++++---------- 2 files changed, 69 insertions(+), 62 deletions(-) diff --git a/graphdriver/devmapper/devmapper_wrapper.go b/graphdriver/devmapper/devmapper_wrapper.go index 1b636d392f..3c195f72ea 100644 --- a/graphdriver/devmapper/devmapper_wrapper.go +++ b/graphdriver/devmapper/devmapper_wrapper.go @@ -54,21 +54,24 @@ type ( ) // FIXME: Make sure the values are defined in C +// IOCTL consts const ( + BlkGetSize64 = C.BLKGETSIZE64 + LoopSetFd = C.LOOP_SET_FD LoopCtlGetFree = C.LOOP_CTL_GET_FREE LoopGetStatus64 = C.LOOP_GET_STATUS64 LoopSetStatus64 = C.LOOP_SET_STATUS64 LoopClrFd = C.LOOP_CLR_FD LoopSetCapacity = C.LOOP_SET_CAPACITY +) +const ( LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY LoFlagsPartScan = C.LO_FLAGS_PARTSCAN LoKeySize = C.LO_KEY_SIZE LoNameSize = C.LO_NAME_SIZE - - BlkGetSize64 = C.BLKGETSIZE64 ) var ( diff --git a/graphdriver/devmapper/driver_test.go b/graphdriver/devmapper/driver_test.go index 3204575dd9..3950152fb7 100644 --- a/graphdriver/devmapper/driver_test.go +++ b/graphdriver/devmapper/driver_test.go @@ -55,12 +55,6 @@ func denyAllDevmapper() { DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr { panic("DmGetNextTarget: this method should not be called here") } - DmAttachLoopDevice = func(filename string, fd *int) string { - panic("DmAttachLoopDevice: this method should not be called here") - } - DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { - panic("DmGetBlockSize: this method should not be called here") - } DmUdevWait = func(cookie uint) int { panic("DmUdevWait: this method should not be called here") } @@ -76,9 +70,6 @@ func denyAllDevmapper() { DmTaskDestroy = func(task *CDmTask) { panic("DmTaskDestroy: this method should not be called here") } - GetBlockSize = func(fd uintptr, size *uint64) sysErrno { - panic("GetBlockSize: this method should not be called here") - } LogWithErrnoInit = func() { panic("LogWithErrnoInit: this method should not be called here") } @@ -155,11 +146,10 @@ func (r Set) Assert(t *testing.T, names ...string) { func TestInit(t *testing.T) { var ( - calls = make(Set) - devicesAttached = make(Set) - taskMessages = make(Set) - taskTypes = make(Set) - home = mkTestDirectory(t) + calls = make(Set) + taskMessages = make(Set) + taskTypes = make(Set) + home = mkTestDirectory(t) ) defer osRemoveAll(home) @@ -233,29 +223,6 @@ func TestInit(t *testing.T) { taskMessages[message] = true return 1 } - var ( - fakeDataLoop = "/dev/loop42" - fakeMetadataLoop = "/dev/loop43" - fakeDataLoopFd = 42 - fakeMetadataLoopFd = 43 - ) - var attachCount int - DmAttachLoopDevice = func(filename string, fd *int) string { - calls["DmAttachLoopDevice"] = true - if _, exists := devicesAttached[filename]; exists { - t.Fatalf("Already attached %s", filename) - } - devicesAttached[filename] = true - // This will crash if fd is not dereferenceable - if attachCount == 0 { - attachCount++ - *fd = fakeDataLoopFd - return fakeDataLoop - } else { - *fd = fakeMetadataLoopFd - return fakeMetadataLoop - } - } DmTaskDestroy = func(task *CDmTask) { calls["DmTaskDestroy"] = true expectedTask := &task1 @@ -263,14 +230,6 @@ func TestInit(t *testing.T) { t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) } } - fakeBlockSize := int64(4242 * 512) - DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { - calls["DmGetBlockSize"] = true - if expectedFd := uintptr(42); fd != expectedFd { - t.Fatalf("Wrong libdevmapper call\nExpected: DmGetBlockSize(%v)\nReceived: DmGetBlockSize(%v)\n", expectedFd, fd) - } - return fakeBlockSize, 0 - } DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { calls["DmTaskSetTarget"] = true expectedTask := &task1 @@ -345,11 +304,9 @@ func TestInit(t *testing.T) { "DmTaskSetName", "DmTaskRun", "DmTaskGetInfo", - "DmAttachLoopDevice", "DmTaskDestroy", "execRun", "DmTaskCreate", - "DmGetBlockSize", "DmTaskSetTarget", "DmTaskSetCookie", "DmUdevWait", @@ -357,7 +314,6 @@ func TestInit(t *testing.T) { "DmTaskSetMessage", "DmTaskSetAddNode", ) - devicesAttached.Assert(t, path.Join(home, "devicemapper", "data"), path.Join(home, "devicemapper", "metadata")) taskTypes.Assert(t, "0", "6", "17") taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1") } @@ -408,17 +364,9 @@ func mockAllDevmapper(calls Set) { calls["DmTaskSetMessage"] = true return 1 } - DmAttachLoopDevice = func(filename string, fd *int) string { - calls["DmAttachLoopDevice"] = true - return "/dev/loop42" - } DmTaskDestroy = func(task *CDmTask) { calls["DmTaskDestroy"] = true } - DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { - calls["DmGetBlockSize"] = true - return int64(4242 * 512), 0 - } DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { calls["DmTaskSetTarget"] = true return 1 @@ -489,6 +437,32 @@ func TestDriverCreate(t *testing.T) { return false, nil } + sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + calls["sysSyscall"] = true + if trap != sysSysIoctl { + t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap) + } + switch a2 { + case LoopSetFd: + calls["ioctl.loopsetfd"] = true + case LoopCtlGetFree: + calls["ioctl.loopctlgetfree"] = true + case LoopGetStatus64: + calls["ioctl.loopgetstatus"] = true + case LoopSetStatus64: + calls["ioctl.loopsetstatus"] = true + case LoopClrFd: + calls["ioctl.loopclrfd"] = true + case LoopSetCapacity: + calls["ioctl.loopsetcapacity"] = true + case BlkGetSize64: + calls["ioctl.blkgetsize"] = true + default: + t.Fatalf("Unexpected IOCTL. Received %d", a2) + } + return 0, 0, 0 + } + func() { d := newDriver(t) @@ -498,16 +472,18 @@ func TestDriverCreate(t *testing.T) { "DmTaskSetName", "DmTaskRun", "DmTaskGetInfo", - "DmAttachLoopDevice", "execRun", "DmTaskCreate", - "DmGetBlockSize", "DmTaskSetTarget", "DmTaskSetCookie", "DmUdevWait", "DmTaskSetSector", "DmTaskSetMessage", "DmTaskSetAddNode", + "sysSyscall", + "ioctl.blkgetsize", + "ioctl.loopsetfd", + "ioctl.loopsetstatus", ) if err := d.Create("1", ""); err != nil { @@ -579,6 +555,32 @@ func TestDriverRemove(t *testing.T) { return false, nil } + sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + calls["sysSyscall"] = true + if trap != sysSysIoctl { + t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap) + } + switch a2 { + case LoopSetFd: + calls["ioctl.loopsetfd"] = true + case LoopCtlGetFree: + calls["ioctl.loopctlgetfree"] = true + case LoopGetStatus64: + calls["ioctl.loopgetstatus"] = true + case LoopSetStatus64: + calls["ioctl.loopsetstatus"] = true + case LoopClrFd: + calls["ioctl.loopclrfd"] = true + case LoopSetCapacity: + calls["ioctl.loopsetcapacity"] = true + case BlkGetSize64: + calls["ioctl.blkgetsize"] = true + default: + t.Fatalf("Unexpected IOCTL. Received %d", a2) + } + return 0, 0, 0 + } + func() { d := newDriver(t) @@ -588,16 +590,18 @@ func TestDriverRemove(t *testing.T) { "DmTaskSetName", "DmTaskRun", "DmTaskGetInfo", - "DmAttachLoopDevice", "execRun", "DmTaskCreate", - "DmGetBlockSize", "DmTaskSetTarget", "DmTaskSetCookie", "DmUdevWait", "DmTaskSetSector", "DmTaskSetMessage", "DmTaskSetAddNode", + "sysSyscall", + "ioctl.blkgetsize", + "ioctl.loopsetfd", + "ioctl.loopsetstatus", ) if err := d.Create("1", ""); err != nil {