File docker-mount-secrets.patch of Package docker.2434

Index: docker-1.10.0/daemon/start.go
===================================================================
--- docker-1.10.0.orig/daemon/start.go
+++ docker-1.10.0/daemon/start.go
@@ -1,12 +1,17 @@
 package daemon
 
 import (
+	"fmt"
+	"os"
+	"path/filepath"
 	"runtime"
+	"syscall"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/container"
 	derr "github.com/docker/docker/errors"
 	"github.com/docker/docker/runconfig"
+	"github.com/docker/docker/vendor/src/github.com/opencontainers/runc/libcontainer/label"
 	containertypes "github.com/docker/engine-api/types/container"
 )
 
@@ -134,6 +139,10 @@ func (daemon *Daemon) containerStart(con
 		}
 	}
 
+	if err := daemon.setupSecretFiles(container); err != nil {
+		return err
+	}
+
 	mounts, err := daemon.setupMounts(container)
 	if err != nil {
 		return err
@@ -142,13 +151,96 @@ func (daemon *Daemon) containerStart(con
 	mounts = append(mounts, container.TmpfsMounts()...)
 
 	container.Command.Mounts = mounts
+
 	if err := daemon.waitForStart(container); err != nil {
 		return err
 	}
+
+	// Now the container is running, unmount the secrets on the host
+	if err := daemon.UnmountSecrets(container, false); err != nil {
+		return err
+	}
+
 	container.HasBeenStartedBefore = true
 	return nil
 }
 
+// unmount secrets on the host. Performs a lazy unmount by default unless
+// `force` is set to true.
+// No unmount operation is invoked if the secrets mount point has already been
+// unmounted.
+func (daemon *Daemon) UnmountSecrets(container *container.Container, force bool) error {
+	secretsPath, err := daemon.secretsPath(container)
+	if err != nil {
+		return err
+	}
+
+	logrus.WithFields(logrus.Fields{
+		"container": container.ID,
+		"path":      secretsPath,
+		"force":     force,
+	}).Debug("SUSE:secrets -> unmounting container secrets")
+
+	var stat_dot, stat_dot_dot syscall.Stat_t
+	if err := syscall.Stat(secretsPath, &stat_dot); err != nil {
+		return fmt.Errorf("Something went wrong while getting stats for dot: %v", err)
+	}
+	if err := syscall.Stat(filepath.Join(secretsPath, ".."), &stat_dot_dot); err != nil {
+		return fmt.Errorf("Something went wrong while getting stats for dot dot: %v", err)
+	}
+
+	// Compare device IDs for /<secretsPath>/. and /<secretsPath>/..
+	// If the device IDs are different then the secrets directory is actually
+	// mounted. Otherwise it has already been unmounted, hence there's nothing
+	// to do (calling unmount would return an error)
+	if stat_dot.Dev != stat_dot_dot.Dev {
+		// By default perform lazy unmount
+		flag := syscall.MNT_DETACH
+		if force {
+			flag = syscall.MNT_FORCE
+		}
+		if err := syscall.Unmount(secretsPath, flag); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (daemon *Daemon) secretsPath(container *container.Container) (string, error) {
+	return container.GetRootResourcePath("secrets")
+}
+
+func (daemon *Daemon) setupSecretFiles(container *container.Container) error {
+	secretsPath, err := daemon.secretsPath(container)
+	if err != nil {
+		return err
+	}
+
+	logrus.WithFields(logrus.Fields{
+		"container": container.ID,
+		"path":      secretsPath,
+	}).Debug("SUSE:secrets -> setting up container secrets")
+
+	if err := os.MkdirAll(secretsPath, 0700); err != nil {
+		return err
+	}
+
+	if err := syscall.Mount("tmpfs", secretsPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel("", container.GetMountLabel())); err != nil {
+		return fmt.Errorf("mounting secret tmpfs: %s", err)
+	}
+
+	data, err := getHostSecretData()
+	if err != nil {
+		return err
+	}
+	for _, s := range data {
+		s.SaveTo(secretsPath)
+	}
+
+	return nil
+}
+
 func (daemon *Daemon) waitForStart(container *container.Container) error {
 	return container.StartMonitor(daemon, container.HostConfig.RestartPolicy)
 }
Index: docker-1.10.0/daemon/delete.go
===================================================================
--- docker-1.10.0.orig/daemon/delete.go
+++ docker-1.10.0/daemon/delete.go
@@ -122,6 +122,17 @@ func (daemon *Daemon) cleanupContainer(c
 		}
 	}()
 
+	// Force unmount of the secrets tmpfs storage added by SUSE's Docker daemon.
+	// This is unmounted automatically at container start time, however the unmount
+	// is done with the 'lazy' flag. This can introduce some race conditions, for
+	// example when the container dies immediately (e.g. wrong entry point). In
+	// that case the secrets directory has not been unmounted yet, causing the
+	// removal of the container to fail because the file system is still reported
+	// as in use. See bnc#954797
+	if err = daemon.UnmountSecrets(container, true); err != nil {
+		logrus.Errorf("SUSE:secrets -> Error unmounting secrets in cleanup: %v", err)
+	}
+
 	if err = os.RemoveAll(container.Root); err != nil {
 		return derr.ErrorCodeRmFS.WithArgs(container.ID, err)
 	}
Index: docker-1.10.0/daemon/volumes_unix.go
===================================================================
--- docker-1.10.0.orig/daemon/volumes_unix.go
+++ docker-1.10.0/daemon/volumes_unix.go
@@ -7,6 +7,7 @@ import (
 	"sort"
 	"strconv"
 
+	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/volume"
@@ -18,6 +19,29 @@ import (
 // calls Setup() on each. It also looks to see if is a network mount such as
 // /etc/resolv.conf, and if it is not, appends it to the array of mounts.
 func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.Mount, error) {
+	if _, exists := container.MountPoints["/run/secrets"]; !exists {
+		const (
+			name = "suse:secrets"
+			dest = "/run/secrets"
+			rw   = true
+		)
+
+		secretsPath, err := daemon.secretsPath(container)
+		if err != nil {
+			return nil, err
+		}
+
+		logrus.WithFields(logrus.Fields{
+			"name":      name,
+			"rw":        rw,
+			"path":      secretsPath,
+			"dest":      dest,
+			"container": container.ID,
+		}).Debug("SUSE:secrets -> adding /run/secrets to bind-mount points")
+
+		container.AddBindMountPoint(name, secretsPath, dest, rw)
+	}
+
 	var mounts []execdriver.Mount
 	for _, m := range container.MountPoints {
 		if err := daemon.lazyInitializeVolume(container.ID, m); err != nil {
Index: docker-1.10.0/daemon/secrets.go
===================================================================
--- /dev/null
+++ docker-1.10.0/daemon/secrets.go
@@ -0,0 +1,103 @@
+package daemon
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	log "github.com/Sirupsen/logrus"
+)
+
+type Secret struct {
+	Name      string
+	IsDir     bool
+	HostBased bool
+}
+
+type SecretData struct {
+	Name string
+	Data []byte
+}
+
+func (s SecretData) SaveTo(dir string) error {
+	path := filepath.Join(dir, s.Name)
+	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) {
+		return err
+	}
+	if err := ioutil.WriteFile(path, s.Data, 0755); err != nil {
+		return err
+	}
+	return nil
+}
+
+func readAll(root, prefix string) ([]SecretData, error) {
+	path := filepath.Join(root, prefix)
+
+	data := []SecretData{}
+
+	files, err := ioutil.ReadDir(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return data, nil
+		}
+
+		return nil, err
+	}
+
+	for _, f := range files {
+		fileData, err := readFile(root, filepath.Join(prefix, f.Name()))
+		if err != nil {
+			// If the file did not exist, might be a dangling symlink
+			// Ignore the error
+			if os.IsNotExist(err) {
+				continue
+			}
+			return nil, err
+		}
+		data = append(data, fileData...)
+	}
+
+	return data, nil
+}
+
+func readFile(root, name string) ([]SecretData, error) {
+	path := filepath.Join(root, name)
+
+	s, err := os.Stat(path)
+	if err != nil {
+		return nil, err
+	}
+
+	if s.IsDir() {
+		dirData, err := readAll(root, name)
+		if err != nil {
+			return nil, err
+		}
+		return dirData, nil
+	} else {
+		bytes, err := ioutil.ReadFile(path)
+		if err != nil {
+			return nil, err
+		}
+		return []SecretData{{Name: name, Data: bytes}}, nil
+	}
+}
+
+func getHostSecretData() ([]SecretData, error) {
+	credentials, err := readAll("/etc/zypp/", "credentials.d")
+	if err != nil {
+		log.Errorf("Error while reading zypp credentials: %s", err)
+		return credentials, err
+	}
+
+	suseConnect, err := readFile("/etc", "SUSEConnect")
+	if err != nil {
+		if os.IsNotExist(err) {
+			suseConnect = []SecretData{}
+		} else {
+			log.Errorf("Error while reading /etc/SUSEConnect: %s", err)
+			return nil, err
+		}
+	}
+	return append(credentials, suseConnect...), nil
+}