mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #14694 from Microsoft/10662-apparmorprofile
Windows: Factoring out unused fields
This commit is contained in:
commit
4ed3e3a5b2
26 changed files with 649 additions and 544 deletions
|
@ -1205,12 +1205,8 @@ func (s *Server) getContainersByName(version version.Version, w http.ResponseWri
|
|||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
if version.LessThan("1.20") {
|
||||
containerJSONRaw, err := s.daemon.ContainerInspectPre120(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, containerJSONRaw)
|
||||
if version.LessThan("1.20") && runtime.GOOS != "windows" {
|
||||
return getContainersByNameDownlevel(w, s, vars["name"])
|
||||
}
|
||||
|
||||
containerJSON, err := s.daemon.ContainerInspect(vars["name"])
|
||||
|
|
|
@ -121,3 +121,13 @@ func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
|
||||
// is only relevant on non-Windows daemons.
|
||||
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
|
||||
containerJSONRaw, err := s.daemon.ContainerInspectPre120(namevar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, containerJSONRaw)
|
||||
}
|
||||
|
|
|
@ -60,3 +60,9 @@ func allocateDaemonPort(addr string) error {
|
|||
|
||||
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
||||
}
|
||||
|
||||
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
|
||||
// is only relevant on non-Windows daemons.
|
||||
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -250,7 +250,8 @@ type ContainerJSON struct {
|
|||
Config *runconfig.Config
|
||||
}
|
||||
|
||||
// backcompatibility struct along with ContainerConfig
|
||||
// backcompatibility struct along with ContainerConfig. Note this is not
|
||||
// used by the Windows daemon.
|
||||
type ContainerJSONPre120 struct {
|
||||
*ContainerJSONBase
|
||||
Volumes map[string]string
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
// path does not refer to a directory.
|
||||
var ErrExtractPointNotDirectory = errors.New("extraction point is not a directory")
|
||||
|
||||
// ContainerCopy performs a depracated operation of archiving the resource at
|
||||
// ContainerCopy performs a deprecated operation of archiving the resource at
|
||||
// the specified path in the conatiner identified by the given name.
|
||||
func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) {
|
||||
container, err := daemon.Get(name)
|
||||
|
@ -25,7 +25,7 @@ func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if res[0] == '/' {
|
||||
if res[0] == '/' || res[0] == '\\' {
|
||||
res = res[1:]
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ func (container *Container) StatPath(path string) (stat *types.ContainerPathStat
|
|||
// Consider the given path as an absolute path in the container.
|
||||
absPath := path
|
||||
if !filepath.IsAbs(absPath) {
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path)
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(os.PathSeparator), path), path)
|
||||
}
|
||||
|
||||
resolvedPath, err := container.GetResourcePath(absPath)
|
||||
|
@ -157,7 +157,7 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta
|
|||
// Consider the given path as an absolute path in the container.
|
||||
absPath := path
|
||||
if !filepath.IsAbs(absPath) {
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path)
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(os.PathSeparator), path), path)
|
||||
}
|
||||
|
||||
resolvedPath, err := container.GetResourcePath(absPath)
|
||||
|
@ -230,7 +230,7 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||
// Consider the given path as an absolute path in the container.
|
||||
absPath := path
|
||||
if !filepath.IsAbs(absPath) {
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path)
|
||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(os.PathSeparator), path), path)
|
||||
}
|
||||
|
||||
resolvedPath, err := container.GetResourcePath(absPath)
|
||||
|
@ -261,19 +261,14 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absPath = filepath.Join("/", baseRel)
|
||||
absPath = filepath.Join(string(os.PathSeparator), baseRel)
|
||||
|
||||
// Need to check if the path is in a volume. If it is, it cannot be in a
|
||||
// read-only volume. If it is not in a volume, the container cannot be
|
||||
// configured with a read-only rootfs.
|
||||
var toVolume bool
|
||||
for _, mnt := range container.MountPoints {
|
||||
if toVolume = mnt.hasResource(absPath); toVolume {
|
||||
if mnt.RW {
|
||||
break
|
||||
}
|
||||
return ErrVolumeReadonly
|
||||
}
|
||||
toVolume, err := checkIfPathIsInAVolume(container, absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !toVolume && container.hostConfig.ReadonlyRootfs {
|
||||
|
|
19
daemon/archive_unix.go
Normal file
19
daemon/archive_unix.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// +build !windows
|
||||
|
||||
package daemon
|
||||
|
||||
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
||||
// cannot be in a read-only volume. If it is not in a volume, the container
|
||||
// cannot be configured with a read-only rootfs.
|
||||
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
|
||||
var toVolume bool
|
||||
for _, mnt := range container.MountPoints {
|
||||
if toVolume = mnt.hasResource(absPath); toVolume {
|
||||
if mnt.RW {
|
||||
break
|
||||
}
|
||||
return false, ErrVolumeReadonly
|
||||
}
|
||||
}
|
||||
return toVolume, nil
|
||||
}
|
10
daemon/archive_windows.go
Normal file
10
daemon/archive_windows.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package daemon
|
||||
|
||||
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
||||
// cannot be in a read-only volume. If it is not in a volume, the container
|
||||
// cannot be configured with a read-only rootfs.
|
||||
//
|
||||
// This is a no-op on Windows which does not support volumes.
|
||||
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
|
@ -17,11 +17,9 @@ type CommonConfig struct {
|
|||
AutoRestart bool
|
||||
Bridge bridgeConfig // Bridge holds bridge network specific configuration.
|
||||
Context map[string][]string
|
||||
CorsHeaders string
|
||||
DisableBridge bool
|
||||
Dns []string
|
||||
DnsSearch []string
|
||||
EnableCors bool
|
||||
ExecDriver string
|
||||
ExecOptions []string
|
||||
ExecRoot string
|
||||
|
@ -51,8 +49,6 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
|
|||
cmd.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", usageFn("Storage driver to use"))
|
||||
cmd.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, usageFn("Exec driver to use"))
|
||||
cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU"))
|
||||
cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header"))
|
||||
cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
|
||||
// FIXME: why the inconsistency between "hosts" and "sockets"?
|
||||
cmd.Var(opts.NewListOptsRef(&config.Dns, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use"))
|
||||
cmd.Var(opts.NewListOptsRef(&config.DnsSearch, opts.ValidateDNSSearch), []string{"-dns-search"}, usageFn("DNS search domains to use"))
|
||||
|
|
|
@ -22,6 +22,8 @@ type Config struct {
|
|||
|
||||
// Fields below here are platform specific.
|
||||
|
||||
CorsHeaders string
|
||||
EnableCors bool
|
||||
EnableSelinuxSupport bool
|
||||
SocketGroup string
|
||||
Ulimits map[string]*ulimit.Ulimit
|
||||
|
@ -71,6 +73,8 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin
|
|||
cmd.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, usageFn("Enable inter-container communication"))
|
||||
cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultIP, "0.0.0.0"), []string{"#ip", "-ip"}, usageFn("Default IP when binding container ports"))
|
||||
cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
|
||||
cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header"))
|
||||
cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
|
||||
|
||||
config.attachExperimentalFlags(cmd, usageFn)
|
||||
}
|
||||
|
|
|
@ -64,28 +64,18 @@ type CommonContainer struct {
|
|||
Config *runconfig.Config
|
||||
ImageID string `json:"Image"`
|
||||
NetworkSettings *network.Settings
|
||||
ResolvConfPath string
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
LogPath string
|
||||
Name string
|
||||
Driver string
|
||||
ExecDriver string
|
||||
MountLabel, ProcessLabel string
|
||||
RestartCount int
|
||||
UpdateDns bool
|
||||
HasBeenStartedBefore bool
|
||||
|
||||
MountPoints map[string]*mountPoint
|
||||
Volumes map[string]string // Deprecated since 1.7, kept for backwards compatibility
|
||||
VolumesRW map[string]bool // Deprecated since 1.7, kept for backwards compatibility
|
||||
|
||||
hostConfig *runconfig.HostConfig
|
||||
command *execdriver.Command
|
||||
|
||||
monitor *containerMonitor
|
||||
execCommands *execStore
|
||||
daemon *Daemon
|
||||
hostConfig *runconfig.HostConfig
|
||||
command *execdriver.Command
|
||||
monitor *containerMonitor
|
||||
execCommands *execStore
|
||||
daemon *Daemon
|
||||
// logDriver for closing
|
||||
logDriver logger.Logger
|
||||
logCopier *logger.Copier
|
||||
|
@ -1076,94 +1066,6 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
|
|||
return written, err
|
||||
}
|
||||
|
||||
func (container *Container) networkMounts() []execdriver.Mount {
|
||||
var mounts []execdriver.Mount
|
||||
if container.ResolvConfPath != "" {
|
||||
label.SetFileLabel(container.ResolvConfPath, container.MountLabel)
|
||||
mounts = append(mounts, execdriver.Mount{
|
||||
Source: container.ResolvConfPath,
|
||||
Destination: "/etc/resolv.conf",
|
||||
Writable: !container.hostConfig.ReadonlyRootfs,
|
||||
Private: true,
|
||||
})
|
||||
}
|
||||
if container.HostnamePath != "" {
|
||||
label.SetFileLabel(container.HostnamePath, container.MountLabel)
|
||||
mounts = append(mounts, execdriver.Mount{
|
||||
Source: container.HostnamePath,
|
||||
Destination: "/etc/hostname",
|
||||
Writable: !container.hostConfig.ReadonlyRootfs,
|
||||
Private: true,
|
||||
})
|
||||
}
|
||||
if container.HostsPath != "" {
|
||||
label.SetFileLabel(container.HostsPath, container.MountLabel)
|
||||
mounts = append(mounts, execdriver.Mount{
|
||||
Source: container.HostsPath,
|
||||
Destination: "/etc/hosts",
|
||||
Writable: !container.hostConfig.ReadonlyRootfs,
|
||||
Private: true,
|
||||
})
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
func (container *Container) addBindMountPoint(name, source, destination string, rw bool) {
|
||||
container.MountPoints[destination] = &mountPoint{
|
||||
Name: name,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
RW: rw,
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) addLocalMountPoint(name, destination string, rw bool) {
|
||||
container.MountPoints[destination] = &mountPoint{
|
||||
Name: name,
|
||||
Driver: volume.DefaultDriverName,
|
||||
Destination: destination,
|
||||
RW: rw,
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
|
||||
container.MountPoints[destination] = &mountPoint{
|
||||
Name: vol.Name(),
|
||||
Driver: vol.DriverName(),
|
||||
Destination: destination,
|
||||
RW: rw,
|
||||
Volume: vol,
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) isDestinationMounted(destination string) bool {
|
||||
return container.MountPoints[destination] != nil
|
||||
}
|
||||
|
||||
func (container *Container) prepareMountPoints() error {
|
||||
for _, config := range container.MountPoints {
|
||||
if len(config.Driver) > 0 {
|
||||
v, err := createVolume(config.Name, config.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Volume = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) removeMountPoints() error {
|
||||
for _, m := range container.MountPoints {
|
||||
if m.Volume != nil {
|
||||
if err := removeVolume(m.Volume); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) shouldRestart() bool {
|
||||
return container.hostConfig.RestartPolicy.Name == "always" ||
|
||||
(container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
|
||||
|
|
|
@ -27,12 +27,14 @@ import (
|
|||
"github.com/docker/docker/pkg/ulimit"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/devices"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
@ -41,9 +43,15 @@ type Container struct {
|
|||
CommonContainer
|
||||
|
||||
// Fields below here are platform specific.
|
||||
|
||||
AppArmorProfile string
|
||||
activeLinks map[string]*links.Link
|
||||
AppArmorProfile string
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
MountPoints map[string]*mountPoint
|
||||
ResolvConfPath string
|
||||
UpdateDns bool
|
||||
Volumes map[string]string // Deprecated since 1.7, kept for backwards compatibility
|
||||
VolumesRW map[string]bool // Deprecated since 1.7, kept for backwards compatibility
|
||||
}
|
||||
|
||||
func killProcessDirectly(container *Container) error {
|
||||
|
@ -1150,3 +1158,91 @@ func (container *Container) PrepareStorage() error {
|
|||
func (container *Container) CleanupStorage() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) networkMounts() []execdriver.Mount {
|
||||
var mounts []execdriver.Mount
|
||||
if container.ResolvConfPath != "" {
|
||||
label.SetFileLabel(container.ResolvConfPath, container.MountLabel)
|
||||
mounts = append(mounts, execdriver.Mount{
|
||||
Source: container.ResolvConfPath,
|
||||
Destination: "/etc/resolv.conf",
|
||||
Writable: !container.hostConfig.ReadonlyRootfs,
|
||||
Private: true,
|
||||
})
|
||||
}
|
||||
if container.HostnamePath != "" {
|
||||
label.SetFileLabel(container.HostnamePath, container.MountLabel)
|
||||
mounts = append(mounts, execdriver.Mount{
|
||||
Source: container.HostnamePath,
|
||||
Destination: "/etc/hostname",
|
||||
Writable: !container.hostConfig.ReadonlyRootfs,
|
||||
Private: true,
|
||||
})
|
||||
}
|
||||
if container.HostsPath != "" {
|
||||
label.SetFileLabel(container.HostsPath, container.MountLabel)
|
||||
mounts = append(mounts, execdriver.Mount{
|
||||
Source: container.HostsPath,
|
||||
Destination: "/etc/hosts",
|
||||
Writable: !container.hostConfig.ReadonlyRootfs,
|
||||
Private: true,
|
||||
})
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
func (container *Container) addBindMountPoint(name, source, destination string, rw bool) {
|
||||
container.MountPoints[destination] = &mountPoint{
|
||||
Name: name,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
RW: rw,
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) addLocalMountPoint(name, destination string, rw bool) {
|
||||
container.MountPoints[destination] = &mountPoint{
|
||||
Name: name,
|
||||
Driver: volume.DefaultDriverName,
|
||||
Destination: destination,
|
||||
RW: rw,
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
|
||||
container.MountPoints[destination] = &mountPoint{
|
||||
Name: vol.Name(),
|
||||
Driver: vol.DriverName(),
|
||||
Destination: destination,
|
||||
RW: rw,
|
||||
Volume: vol,
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) isDestinationMounted(destination string) bool {
|
||||
return container.MountPoints[destination] != nil
|
||||
}
|
||||
|
||||
func (container *Container) prepareMountPoints() error {
|
||||
for _, config := range container.MountPoints {
|
||||
if len(config.Driver) > 0 {
|
||||
v, err := createVolume(config.Name, config.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Volume = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) removeMountPoints() error {
|
||||
for _, m := range container.MountPoints {
|
||||
if m.Volume != nil {
|
||||
if err := removeVolume(m.Volume); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,18 +22,6 @@ type Container struct {
|
|||
CommonContainer
|
||||
|
||||
// Fields below here are platform specific.
|
||||
|
||||
// TODO Windows. Further factoring out of unused fields will be necessary.
|
||||
|
||||
// ---- START OF TEMPORARY DECLARATION ----
|
||||
// TODO Windows. Temporarily keeping fields in to assist in compilation
|
||||
// of the daemon on Windows without affecting many other files in a single
|
||||
// PR, thus making code review significantly harder. These lines will be
|
||||
// removed in subsequent PRs.
|
||||
|
||||
AppArmorProfile string
|
||||
// ---- END OF TEMPORARY DECLARATION ----
|
||||
|
||||
}
|
||||
|
||||
func killProcessDirectly(container *Container) error {
|
||||
|
@ -213,3 +201,13 @@ func (container *Container) CleanupStorage() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO Windows. This can be further factored out. Used in daemon.go
|
||||
func (container *Container) prepareMountPoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO Windows. This can be further factored out. Used in delete.go
|
||||
func (container *Container) removeMountPoints() error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,15 +2,11 @@ package daemon
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
@ -96,47 +92,10 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||
}
|
||||
defer container.Unmount()
|
||||
|
||||
for spec := range config.Volumes {
|
||||
var (
|
||||
name, destination string
|
||||
parts = strings.Split(spec, ":")
|
||||
)
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
name, destination = parts[0], filepath.Clean(parts[1])
|
||||
default:
|
||||
name = stringid.GenerateRandomID()
|
||||
destination = filepath.Clean(parts[0])
|
||||
}
|
||||
// Skip volumes for which we already have something mounted on that
|
||||
// destination because of a --volume-from.
|
||||
if container.isDestinationMounted(destination) {
|
||||
continue
|
||||
}
|
||||
path, err := container.GetResourcePath(destination)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() {
|
||||
return nil, nil, fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
|
||||
}
|
||||
|
||||
v, err := createVolume(name, config.VolumeDriver)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := label.Relabel(v.Path(), container.MountLabel, "z"); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := container.copyImagePathContent(v, destination); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
container.addMountPointWithVolume(destination, v, true)
|
||||
if err := createContainerPlatformSpecificSettings(container, config); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := container.ToDisk(); err != nil {
|
||||
logrus.Errorf("Error saving new container to disk: %v", err)
|
||||
return nil, nil, err
|
||||
|
|
60
daemon/create_unix.go
Normal file
60
daemon/create_unix.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
// +build !windows
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
// createContainerPlatformSpecificSettings performs platform specific container create functionality
|
||||
func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config) error {
|
||||
for spec := range config.Volumes {
|
||||
var (
|
||||
name, destination string
|
||||
parts = strings.Split(spec, ":")
|
||||
)
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
name, destination = parts[0], filepath.Clean(parts[1])
|
||||
default:
|
||||
name = stringid.GenerateRandomID()
|
||||
destination = filepath.Clean(parts[0])
|
||||
}
|
||||
// Skip volumes for which we already have something mounted on that
|
||||
// destination because of a --volume-from.
|
||||
if container.isDestinationMounted(destination) {
|
||||
continue
|
||||
}
|
||||
path, err := container.GetResourcePath(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() {
|
||||
return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
|
||||
}
|
||||
|
||||
v, err := createVolume(name, config.VolumeDriver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := label.Relabel(v.Path(), container.MountLabel, "z"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := container.copyImagePathContent(v, destination); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
container.addMountPointWithVolume(destination, v, true)
|
||||
}
|
||||
return nil
|
||||
}
|
10
daemon/create_windows.go
Normal file
10
daemon/create_windows.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
// createContainerPlatformSpecificSettings performs platform specific container create functionality
|
||||
func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config) error {
|
||||
return nil
|
||||
}
|
|
@ -144,19 +144,17 @@ func (daemon *Daemon) containerRoot(id string) string {
|
|||
// Load reads the contents of a container from disk
|
||||
// This is typically done at startup.
|
||||
func (daemon *Daemon) load(id string) (*Container, error) {
|
||||
container := &Container{
|
||||
CommonContainer: daemon.newBaseContainer(id),
|
||||
}
|
||||
container := daemon.newBaseContainer(id)
|
||||
|
||||
if err := container.FromDisk(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if container.ID != id {
|
||||
return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
||||
return &container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
||||
}
|
||||
|
||||
return container, nil
|
||||
return &container, nil
|
||||
}
|
||||
|
||||
// Register makes a container object usable by the daemon as <container.ID>
|
||||
|
@ -478,11 +476,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID
|
|||
base.Driver = daemon.driver.String()
|
||||
base.ExecDriver = daemon.execDriver.Name()
|
||||
|
||||
container := &Container{
|
||||
CommonContainer: base,
|
||||
}
|
||||
|
||||
return container, err
|
||||
return &base, err
|
||||
}
|
||||
|
||||
func GetFullContainerName(name string) (string, error) {
|
||||
|
@ -947,18 +941,6 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
|
|||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) newBaseContainer(id string) CommonContainer {
|
||||
return CommonContainer{
|
||||
ID: id,
|
||||
State: NewState(),
|
||||
MountPoints: make(map[string]*mountPoint),
|
||||
Volumes: make(map[string]string),
|
||||
VolumesRW: make(map[string]bool),
|
||||
execCommands: newExecStore(),
|
||||
root: daemon.containerRoot(id),
|
||||
}
|
||||
}
|
||||
|
||||
func setDefaultMtu(config *Config) {
|
||||
// do nothing if the config does not have the default 0 value.
|
||||
if config.Mtu != 0 {
|
||||
|
|
|
@ -539,3 +539,17 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) newBaseContainer(id string) Container {
|
||||
return Container{
|
||||
CommonContainer: CommonContainer{
|
||||
ID: id,
|
||||
State: NewState(),
|
||||
execCommands: newExecStore(),
|
||||
root: daemon.containerRoot(id),
|
||||
},
|
||||
MountPoints: make(map[string]*mountPoint),
|
||||
Volumes: make(map[string]string),
|
||||
VolumesRW: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,3 +169,14 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) newBaseContainer(id string) Container {
|
||||
return Container{
|
||||
CommonContainer: CommonContainer{
|
||||
ID: id,
|
||||
State: NewState(),
|
||||
execCommands: newExecStore(),
|
||||
root: daemon.containerRoot(id),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,53 +21,11 @@ func (daemon *Daemon) ContainerInspect(name string) (*types.ContainerJSON, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||
for _, m := range container.MountPoints {
|
||||
mountPoints = append(mountPoints, types.MountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Path(),
|
||||
Destination: m.Destination,
|
||||
Driver: m.Driver,
|
||||
Mode: m.Mode,
|
||||
RW: m.RW,
|
||||
})
|
||||
}
|
||||
mountPoints := addMountPoints(container)
|
||||
|
||||
return &types.ContainerJSON{base, mountPoints, container.Config}, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONPre120, error) {
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
|
||||
base, err := daemon.getInspectData(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
volumes := make(map[string]string)
|
||||
volumesRW := make(map[string]bool)
|
||||
for _, m := range container.MountPoints {
|
||||
volumes[m.Destination] = m.Path()
|
||||
volumesRW[m.Destination] = m.RW
|
||||
}
|
||||
|
||||
config := &types.ContainerConfig{
|
||||
container.Config,
|
||||
container.hostConfig.Memory,
|
||||
container.hostConfig.MemorySwap,
|
||||
container.hostConfig.CPUShares,
|
||||
container.hostConfig.CpusetCpus,
|
||||
}
|
||||
|
||||
return &types.ContainerJSONPre120{base, volumes, volumesRW, config}, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSONBase, error) {
|
||||
// make a copy to play with
|
||||
hostConfig := *container.hostConfig
|
||||
|
@ -104,9 +62,6 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON
|
|||
State: containerState,
|
||||
Image: container.ImageID,
|
||||
NetworkSettings: container.NetworkSettings,
|
||||
ResolvConfPath: container.ResolvConfPath,
|
||||
HostnamePath: container.HostnamePath,
|
||||
HostsPath: container.HostsPath,
|
||||
LogPath: container.LogPath,
|
||||
Name: container.Name,
|
||||
RestartCount: container.RestartCount,
|
||||
|
@ -114,11 +69,13 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON
|
|||
ExecDriver: container.ExecDriver,
|
||||
MountLabel: container.MountLabel,
|
||||
ProcessLabel: container.ProcessLabel,
|
||||
AppArmorProfile: container.AppArmorProfile,
|
||||
ExecIDs: container.GetExecIDs(),
|
||||
HostConfig: &hostConfig,
|
||||
}
|
||||
|
||||
// Now set any platform-specific fields
|
||||
contJSONBase = setPlatformSpecificContainerFields(container, contJSONBase)
|
||||
|
||||
contJSONBase.GraphDriver.Name = container.Driver
|
||||
graphDriverData, err := daemon.driver.GetMetadata(container.ID)
|
||||
if err != nil {
|
||||
|
|
62
daemon/inspect_unix.go
Normal file
62
daemon/inspect_unix.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
// +build !windows
|
||||
|
||||
package daemon
|
||||
|
||||
import "github.com/docker/docker/api/types"
|
||||
|
||||
// This sets platform-specific fields
|
||||
func setPlatformSpecificContainerFields(container *Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
|
||||
contJSONBase.AppArmorProfile = container.AppArmorProfile
|
||||
contJSONBase.ResolvConfPath = container.ResolvConfPath
|
||||
contJSONBase.HostnamePath = container.HostnamePath
|
||||
contJSONBase.HostsPath = container.HostsPath
|
||||
|
||||
return contJSONBase
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONPre120, error) {
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
|
||||
base, err := daemon.getInspectData(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
volumes := make(map[string]string)
|
||||
volumesRW := make(map[string]bool)
|
||||
for _, m := range container.MountPoints {
|
||||
volumes[m.Destination] = m.Path()
|
||||
volumesRW[m.Destination] = m.RW
|
||||
}
|
||||
|
||||
config := &types.ContainerConfig{
|
||||
container.Config,
|
||||
container.hostConfig.Memory,
|
||||
container.hostConfig.MemorySwap,
|
||||
container.hostConfig.CPUShares,
|
||||
container.hostConfig.CpusetCpus,
|
||||
}
|
||||
|
||||
return &types.ContainerJSONPre120{base, volumes, volumesRW, config}, nil
|
||||
}
|
||||
|
||||
func addMountPoints(container *Container) []types.MountPoint {
|
||||
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||
for _, m := range container.MountPoints {
|
||||
mountPoints = append(mountPoints, types.MountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Path(),
|
||||
Destination: m.Destination,
|
||||
Driver: m.Driver,
|
||||
Mode: m.Mode,
|
||||
RW: m.RW,
|
||||
})
|
||||
}
|
||||
return mountPoints
|
||||
}
|
12
daemon/inspect_windows.go
Normal file
12
daemon/inspect_windows.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package daemon
|
||||
|
||||
import "github.com/docker/docker/api/types"
|
||||
|
||||
// This sets platform-specific fields
|
||||
func setPlatformSpecificContainerFields(container *Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
|
||||
return contJSONBase
|
||||
}
|
||||
|
||||
func addMountPoints(container *Container) []types.MountPoint {
|
||||
return nil
|
||||
}
|
|
@ -8,20 +8,17 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/docker/volume/drivers"
|
||||
"github.com/docker/docker/volume/local"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
// ErrVolumeReadonly is used to signal an error when trying to copy data into
|
||||
// a volume mount that is not writable.
|
||||
var ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
|
||||
|
||||
// TODO Windows. Further platform refactoring can still be done in volumes*.go
|
||||
|
||||
type mountPoint struct {
|
||||
Name string
|
||||
Destination string
|
||||
|
@ -70,73 +67,6 @@ func (m *mountPoint) Path() string {
|
|||
return m.Source
|
||||
}
|
||||
|
||||
// BackwardsCompatible decides whether this mount point can be
|
||||
// used in old versions of Docker or not.
|
||||
// Only bind mounts and local volumes can be used in old versions of Docker.
|
||||
func (m *mountPoint) BackwardsCompatible() bool {
|
||||
return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
|
||||
}
|
||||
|
||||
func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*mountPoint, error) {
|
||||
bind := &mountPoint{
|
||||
RW: true,
|
||||
}
|
||||
arr := strings.Split(spec, ":")
|
||||
|
||||
switch len(arr) {
|
||||
case 2:
|
||||
bind.Destination = arr[1]
|
||||
case 3:
|
||||
bind.Destination = arr[1]
|
||||
mode := arr[2]
|
||||
isValid, isRw := volume.ValidateMountMode(mode)
|
||||
if !isValid {
|
||||
return nil, fmt.Errorf("invalid mode for volumes-from: %s", mode)
|
||||
}
|
||||
bind.RW = isRw
|
||||
// Mode field is used by SELinux to decide whether to apply label
|
||||
bind.Mode = mode
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid volume specification: %s", spec)
|
||||
}
|
||||
|
||||
name, source, err := parseVolumeSource(arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(source) == 0 {
|
||||
bind.Driver = config.VolumeDriver
|
||||
if len(bind.Driver) == 0 {
|
||||
bind.Driver = volume.DefaultDriverName
|
||||
}
|
||||
} else {
|
||||
bind.Source = filepath.Clean(source)
|
||||
}
|
||||
|
||||
bind.Name = name
|
||||
bind.Destination = filepath.Clean(bind.Destination)
|
||||
return bind, nil
|
||||
}
|
||||
|
||||
func parseVolumesFrom(spec string) (string, string, error) {
|
||||
if len(spec) == 0 {
|
||||
return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec)
|
||||
}
|
||||
|
||||
specParts := strings.SplitN(spec, ":", 2)
|
||||
id := specParts[0]
|
||||
mode := "rw"
|
||||
|
||||
if len(specParts) == 2 {
|
||||
mode = specParts[1]
|
||||
if isValid, _ := volume.ValidateMountMode(mode); !isValid {
|
||||
return "", "", fmt.Errorf("invalid mode for volumes-from: %s", mode)
|
||||
}
|
||||
}
|
||||
return id, mode, nil
|
||||
}
|
||||
|
||||
func copyExistingContents(source, destination string) error {
|
||||
volList, err := ioutil.ReadDir(source)
|
||||
if err != nil {
|
||||
|
@ -156,211 +86,3 @@ func copyExistingContents(source, destination string) error {
|
|||
}
|
||||
return copyOwnership(source, destination)
|
||||
}
|
||||
|
||||
// registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
|
||||
// It follows the next sequence to decide what to mount in each final destination:
|
||||
//
|
||||
// 1. Select the previously configured mount points for the containers, if any.
|
||||
// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
|
||||
// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
|
||||
func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
|
||||
binds := map[string]bool{}
|
||||
mountPoints := map[string]*mountPoint{}
|
||||
|
||||
// 1. Read already configured mount points.
|
||||
for name, point := range container.MountPoints {
|
||||
mountPoints[name] = point
|
||||
}
|
||||
|
||||
// 2. Read volumes from other containers.
|
||||
for _, v := range hostConfig.VolumesFrom {
|
||||
containerID, mode, err := parseVolumesFrom(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := daemon.Get(containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range c.MountPoints {
|
||||
cp := &mountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Source,
|
||||
RW: m.RW && volume.ReadWrite(mode),
|
||||
Driver: m.Driver,
|
||||
Destination: m.Destination,
|
||||
}
|
||||
|
||||
if len(cp.Source) == 0 {
|
||||
v, err := createVolume(cp.Name, cp.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cp.Volume = v
|
||||
}
|
||||
|
||||
mountPoints[cp.Destination] = cp
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Read bind mounts
|
||||
for _, b := range hostConfig.Binds {
|
||||
// #10618
|
||||
bind, err := parseBindMount(b, container.MountLabel, container.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if binds[bind.Destination] {
|
||||
return fmt.Errorf("Duplicate bind mount %s", bind.Destination)
|
||||
}
|
||||
|
||||
if len(bind.Name) > 0 && len(bind.Driver) > 0 {
|
||||
// create the volume
|
||||
v, err := createVolume(bind.Name, bind.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bind.Volume = v
|
||||
bind.Source = v.Path()
|
||||
// Since this is just a named volume and not a typical bind, set to shared mode `z`
|
||||
if bind.Mode == "" {
|
||||
bind.Mode = "z"
|
||||
}
|
||||
}
|
||||
|
||||
if err := label.Relabel(bind.Source, container.MountLabel, bind.Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
binds[bind.Destination] = true
|
||||
mountPoints[bind.Destination] = bind
|
||||
}
|
||||
|
||||
// Keep backwards compatible structures
|
||||
bcVolumes := map[string]string{}
|
||||
bcVolumesRW := map[string]bool{}
|
||||
for _, m := range mountPoints {
|
||||
if m.BackwardsCompatible() {
|
||||
bcVolumes[m.Destination] = m.Path()
|
||||
bcVolumesRW[m.Destination] = m.RW
|
||||
}
|
||||
}
|
||||
|
||||
container.Lock()
|
||||
container.MountPoints = mountPoints
|
||||
container.Volumes = bcVolumes
|
||||
container.VolumesRW = bcVolumesRW
|
||||
container.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO Windows. Factor out as not relevant (as Windows daemon support not in pre-1.7)
|
||||
// verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
|
||||
// It reads the container configuration and creates valid mount points for the old volumes.
|
||||
func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
|
||||
// Inspect old structures only when we're upgrading from old versions
|
||||
// to versions >= 1.7 and the MountPoints has not been populated with volumes data.
|
||||
if len(container.MountPoints) == 0 && len(container.Volumes) > 0 {
|
||||
for destination, hostPath := range container.Volumes {
|
||||
vfsPath := filepath.Join(daemon.root, "vfs", "dir")
|
||||
rw := container.VolumesRW != nil && container.VolumesRW[destination]
|
||||
|
||||
if strings.HasPrefix(hostPath, vfsPath) {
|
||||
id := filepath.Base(hostPath)
|
||||
if err := migrateVolume(id, hostPath); err != nil {
|
||||
return err
|
||||
}
|
||||
container.addLocalMountPoint(id, destination, rw)
|
||||
} else { // Bind mount
|
||||
id, source, err := parseVolumeSource(hostPath)
|
||||
// We should not find an error here coming
|
||||
// from the old configuration, but who knows.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.addBindMountPoint(id, source, destination, rw)
|
||||
}
|
||||
}
|
||||
} else if len(container.MountPoints) > 0 {
|
||||
// Volumes created with a Docker version >= 1.7. We verify integrity in case of data created
|
||||
// with Docker 1.7 RC versions that put the information in
|
||||
// DOCKER_ROOT/volumes/VOLUME_ID rather than DOCKER_ROOT/volumes/VOLUME_ID/_container_data.
|
||||
l, err := getVolumeDriver(volume.DefaultDriverName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range container.MountPoints {
|
||||
if m.Driver != volume.DefaultDriverName {
|
||||
continue
|
||||
}
|
||||
dataPath := l.(*local.Root).DataPath(m.Name)
|
||||
volumePath := filepath.Dir(dataPath)
|
||||
|
||||
d, err := ioutil.ReadDir(volumePath)
|
||||
if err != nil {
|
||||
// If the volume directory doesn't exist yet it will be recreated,
|
||||
// so we only return the error when there is a different issue.
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
// Do not check when the volume directory does not exist.
|
||||
continue
|
||||
}
|
||||
if validVolumeLayout(d) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.Mkdir(dataPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Move data inside the data directory
|
||||
for _, f := range d {
|
||||
oldp := filepath.Join(volumePath, f.Name())
|
||||
newp := filepath.Join(dataPath, f.Name())
|
||||
if err := os.Rename(oldp, newp); err != nil {
|
||||
logrus.Errorf("Unable to move %s to %s\n", oldp, newp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return container.ToDisk()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createVolume(name, driverName string) (volume.Volume, error) {
|
||||
vd, err := getVolumeDriver(driverName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vd.Create(name)
|
||||
}
|
||||
|
||||
func removeVolume(v volume.Volume) error {
|
||||
vd, err := getVolumeDriver(v.DriverName())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return vd.Remove(v)
|
||||
}
|
||||
|
||||
func getVolumeDriver(name string) (volume.Driver, error) {
|
||||
if name == "" {
|
||||
name = volume.DefaultDriverName
|
||||
}
|
||||
return volumedrivers.Lookup(name)
|
||||
}
|
||||
|
||||
func parseVolumeSource(spec string) (string, string, error) {
|
||||
if !filepath.IsAbs(spec) {
|
||||
return spec, "", nil
|
||||
}
|
||||
|
||||
return "", spec, nil
|
||||
}
|
||||
|
|
|
@ -3,15 +3,21 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/docker/volume/drivers"
|
||||
"github.com/docker/docker/volume/local"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
// copyOwnership copies the permissions and uid:gid of the source file
|
||||
|
@ -49,6 +55,48 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) {
|
|||
return append(mounts, container.networkMounts()...), nil
|
||||
}
|
||||
|
||||
func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*mountPoint, error) {
|
||||
bind := &mountPoint{
|
||||
RW: true,
|
||||
}
|
||||
arr := strings.Split(spec, ":")
|
||||
|
||||
switch len(arr) {
|
||||
case 2:
|
||||
bind.Destination = arr[1]
|
||||
case 3:
|
||||
bind.Destination = arr[1]
|
||||
mode := arr[2]
|
||||
isValid, isRw := volume.ValidateMountMode(mode)
|
||||
if !isValid {
|
||||
return nil, fmt.Errorf("invalid mode for volumes-from: %s", mode)
|
||||
}
|
||||
bind.RW = isRw
|
||||
// Mode field is used by SELinux to decide whether to apply label
|
||||
bind.Mode = mode
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid volume specification: %s", spec)
|
||||
}
|
||||
|
||||
name, source, err := parseVolumeSource(arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(source) == 0 {
|
||||
bind.Driver = config.VolumeDriver
|
||||
if len(bind.Driver) == 0 {
|
||||
bind.Driver = volume.DefaultDriverName
|
||||
}
|
||||
} else {
|
||||
bind.Source = filepath.Clean(source)
|
||||
}
|
||||
|
||||
bind.Name = name
|
||||
bind.Destination = filepath.Clean(bind.Destination)
|
||||
return bind, nil
|
||||
}
|
||||
|
||||
func sortMounts(m []execdriver.Mount) []execdriver.Mount {
|
||||
sort.Sort(mounts(m))
|
||||
return m
|
||||
|
@ -118,3 +166,235 @@ func validVolumeLayout(files []os.FileInfo) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
// verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
|
||||
// It reads the container configuration and creates valid mount points for the old volumes.
|
||||
func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
|
||||
// Inspect old structures only when we're upgrading from old versions
|
||||
// to versions >= 1.7 and the MountPoints has not been populated with volumes data.
|
||||
if len(container.MountPoints) == 0 && len(container.Volumes) > 0 {
|
||||
for destination, hostPath := range container.Volumes {
|
||||
vfsPath := filepath.Join(daemon.root, "vfs", "dir")
|
||||
rw := container.VolumesRW != nil && container.VolumesRW[destination]
|
||||
|
||||
if strings.HasPrefix(hostPath, vfsPath) {
|
||||
id := filepath.Base(hostPath)
|
||||
if err := migrateVolume(id, hostPath); err != nil {
|
||||
return err
|
||||
}
|
||||
container.addLocalMountPoint(id, destination, rw)
|
||||
} else { // Bind mount
|
||||
id, source, err := parseVolumeSource(hostPath)
|
||||
// We should not find an error here coming
|
||||
// from the old configuration, but who knows.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.addBindMountPoint(id, source, destination, rw)
|
||||
}
|
||||
}
|
||||
} else if len(container.MountPoints) > 0 {
|
||||
// Volumes created with a Docker version >= 1.7. We verify integrity in case of data created
|
||||
// with Docker 1.7 RC versions that put the information in
|
||||
// DOCKER_ROOT/volumes/VOLUME_ID rather than DOCKER_ROOT/volumes/VOLUME_ID/_container_data.
|
||||
l, err := getVolumeDriver(volume.DefaultDriverName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range container.MountPoints {
|
||||
if m.Driver != volume.DefaultDriverName {
|
||||
continue
|
||||
}
|
||||
dataPath := l.(*local.Root).DataPath(m.Name)
|
||||
volumePath := filepath.Dir(dataPath)
|
||||
|
||||
d, err := ioutil.ReadDir(volumePath)
|
||||
if err != nil {
|
||||
// If the volume directory doesn't exist yet it will be recreated,
|
||||
// so we only return the error when there is a different issue.
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
// Do not check when the volume directory does not exist.
|
||||
continue
|
||||
}
|
||||
if validVolumeLayout(d) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.Mkdir(dataPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Move data inside the data directory
|
||||
for _, f := range d {
|
||||
oldp := filepath.Join(volumePath, f.Name())
|
||||
newp := filepath.Join(dataPath, f.Name())
|
||||
if err := os.Rename(oldp, newp); err != nil {
|
||||
logrus.Errorf("Unable to move %s to %s\n", oldp, newp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return container.ToDisk()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseVolumesFrom(spec string) (string, string, error) {
|
||||
if len(spec) == 0 {
|
||||
return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec)
|
||||
}
|
||||
|
||||
specParts := strings.SplitN(spec, ":", 2)
|
||||
id := specParts[0]
|
||||
mode := "rw"
|
||||
|
||||
if len(specParts) == 2 {
|
||||
mode = specParts[1]
|
||||
if isValid, _ := volume.ValidateMountMode(mode); !isValid {
|
||||
return "", "", fmt.Errorf("invalid mode for volumes-from: %s", mode)
|
||||
}
|
||||
}
|
||||
return id, mode, nil
|
||||
}
|
||||
|
||||
// registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
|
||||
// It follows the next sequence to decide what to mount in each final destination:
|
||||
//
|
||||
// 1. Select the previously configured mount points for the containers, if any.
|
||||
// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
|
||||
// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
|
||||
func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
|
||||
binds := map[string]bool{}
|
||||
mountPoints := map[string]*mountPoint{}
|
||||
|
||||
// 1. Read already configured mount points.
|
||||
for name, point := range container.MountPoints {
|
||||
mountPoints[name] = point
|
||||
}
|
||||
|
||||
// 2. Read volumes from other containers.
|
||||
for _, v := range hostConfig.VolumesFrom {
|
||||
containerID, mode, err := parseVolumesFrom(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := daemon.Get(containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range c.MountPoints {
|
||||
cp := &mountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Source,
|
||||
RW: m.RW && volume.ReadWrite(mode),
|
||||
Driver: m.Driver,
|
||||
Destination: m.Destination,
|
||||
}
|
||||
|
||||
if len(cp.Source) == 0 {
|
||||
v, err := createVolume(cp.Name, cp.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cp.Volume = v
|
||||
}
|
||||
|
||||
mountPoints[cp.Destination] = cp
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Read bind mounts
|
||||
for _, b := range hostConfig.Binds {
|
||||
// #10618
|
||||
bind, err := parseBindMount(b, container.MountLabel, container.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if binds[bind.Destination] {
|
||||
return fmt.Errorf("Duplicate bind mount %s", bind.Destination)
|
||||
}
|
||||
|
||||
if len(bind.Name) > 0 && len(bind.Driver) > 0 {
|
||||
// create the volume
|
||||
v, err := createVolume(bind.Name, bind.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bind.Volume = v
|
||||
bind.Source = v.Path()
|
||||
// Since this is just a named volume and not a typical bind, set to shared mode `z`
|
||||
if bind.Mode == "" {
|
||||
bind.Mode = "z"
|
||||
}
|
||||
}
|
||||
|
||||
if err := label.Relabel(bind.Source, container.MountLabel, bind.Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
binds[bind.Destination] = true
|
||||
mountPoints[bind.Destination] = bind
|
||||
}
|
||||
|
||||
// Keep backwards compatible structures
|
||||
bcVolumes := map[string]string{}
|
||||
bcVolumesRW := map[string]bool{}
|
||||
for _, m := range mountPoints {
|
||||
if m.BackwardsCompatible() {
|
||||
bcVolumes[m.Destination] = m.Path()
|
||||
bcVolumesRW[m.Destination] = m.RW
|
||||
}
|
||||
}
|
||||
|
||||
container.Lock()
|
||||
container.MountPoints = mountPoints
|
||||
container.Volumes = bcVolumes
|
||||
container.VolumesRW = bcVolumesRW
|
||||
container.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createVolume(name, driverName string) (volume.Volume, error) {
|
||||
vd, err := getVolumeDriver(driverName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vd.Create(name)
|
||||
}
|
||||
|
||||
func removeVolume(v volume.Volume) error {
|
||||
vd, err := getVolumeDriver(v.DriverName())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return vd.Remove(v)
|
||||
}
|
||||
|
||||
func getVolumeDriver(name string) (volume.Driver, error) {
|
||||
if name == "" {
|
||||
name = volume.DefaultDriverName
|
||||
}
|
||||
return volumedrivers.Lookup(name)
|
||||
}
|
||||
|
||||
func parseVolumeSource(spec string) (string, string, error) {
|
||||
if !filepath.IsAbs(spec) {
|
||||
return spec, "", nil
|
||||
}
|
||||
|
||||
return "", spec, nil
|
||||
}
|
||||
|
||||
// BackwardsCompatible decides whether this mount point can be
|
||||
// used in old versions of Docker or not.
|
||||
// Only bind mounts and local volumes can be used in old versions of Docker.
|
||||
func (m *mountPoint) BackwardsCompatible() bool {
|
||||
return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
// Not supported on Windows
|
||||
|
@ -17,10 +16,13 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func migrateVolume(id, vfs string) error {
|
||||
// verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
|
||||
// As the Windows daemon was not supported before 1.7, this is a no-op
|
||||
func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validVolumeLayout(files []os.FileInfo) bool {
|
||||
return true
|
||||
// TODO Windows: This can be further factored out. Called from daemon\daemon.go
|
||||
func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -212,10 +212,8 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
|
|||
}
|
||||
|
||||
serverConfig := &apiserver.ServerConfig{
|
||||
Logging: true,
|
||||
EnableCors: cli.EnableCors,
|
||||
CorsHeaders: cli.CorsHeaders,
|
||||
Version: dockerversion.VERSION,
|
||||
Logging: true,
|
||||
Version: dockerversion.VERSION,
|
||||
}
|
||||
serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ import (
|
|||
|
||||
func setPlatformServerConfig(serverConfig *apiserver.ServerConfig, daemonCfg *daemon.Config) *apiserver.ServerConfig {
|
||||
serverConfig.SocketGroup = daemonCfg.SocketGroup
|
||||
serverConfig.EnableCors = daemonCfg.EnableCors
|
||||
serverConfig.CorsHeaders = daemonCfg.CorsHeaders
|
||||
|
||||
return serverConfig
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue