瀏覽代碼

Merge pull request #33169 from johnstep/windows-configs

Add Windows configs support
John Stephens 8 年之前
父節點
當前提交
7658851e74

+ 38 - 0
container/container_windows.go

@@ -14,6 +14,7 @@ import (
 const (
 	containerSecretMountPath         = `C:\ProgramData\Docker\secrets`
 	containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets`
+	containerInternalConfigsDirPath  = `C:\ProgramData\Docker\internal\configs`
 )
 
 // Container holds fields specific to the Windows implementation. See
@@ -91,6 +92,43 @@ func (container *Container) UnmountSecrets() error {
 	return os.RemoveAll(container.SecretMountPath())
 }
 
+// CreateConfigSymlinks creates symlinks to files in the config mount.
+func (container *Container) CreateConfigSymlinks() error {
+	for _, configRef := range container.ConfigReferences {
+		if configRef.File == nil {
+			continue
+		}
+		resolvedPath, _, err := container.ResolvePath(configRef.File.Name)
+		if err != nil {
+			return err
+		}
+		if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil {
+			return err
+		}
+		if err := os.Symlink(filepath.Join(containerInternalConfigsDirPath, configRef.ConfigID), resolvedPath); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ConfigMounts returns the mount for configs.
+// All configs are stored in a single mount on Windows. Target symlinks are
+// created for each config, pointing to the files in this mount.
+func (container *Container) ConfigMounts() []Mount {
+	var mounts []Mount
+	if len(container.ConfigReferences) > 0 {
+		mounts = append(mounts, Mount{
+			Source:      container.ConfigsDirPath(),
+			Destination: containerInternalConfigsDirPath,
+			Writable:    false,
+		})
+	}
+
+	return mounts
+}
+
 // DetachAndUnmount unmounts all volumes.
 // On Windows it only delegates to `UnmountVolumes` since there is nothing to
 // force unmount.

+ 1 - 1
daemon/configs_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux
+// +build !linux,!windows
 
 package daemon
 

+ 7 - 0
daemon/configs_windows.go

@@ -0,0 +1,7 @@
+// +build windows
+
+package daemon
+
+func configsSupported() bool {
+	return true
+}

+ 49 - 0
daemon/container_operations_windows.go

@@ -16,6 +16,55 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s
 	return nil, nil
 }
 
+func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
+	if len(c.ConfigReferences) == 0 {
+		return nil
+	}
+
+	localPath := c.ConfigsDirPath()
+	logrus.Debugf("configs: setting up config dir: %s", localPath)
+
+	// create local config root
+	if err := system.MkdirAllWithACL(localPath, 0); err != nil {
+		return errors.Wrap(err, "error creating config dir")
+	}
+
+	defer func() {
+		if setupErr != nil {
+			if err := os.RemoveAll(localPath); err != nil {
+				logrus.Errorf("error cleaning up config dir: %s", err)
+			}
+		}
+	}()
+
+	if c.DependencyStore == nil {
+		return fmt.Errorf("config store is not initialized")
+	}
+
+	for _, configRef := range c.ConfigReferences {
+		// TODO (ehazlett): use type switch when more are supported
+		if configRef.File == nil {
+			logrus.Error("config target type is not a file target")
+			continue
+		}
+
+		fPath := c.ConfigFilePath(*configRef)
+
+		log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath})
+
+		log.Debug("injecting config")
+		config := c.DependencyStore.Configs().Get(configRef.ConfigID)
+		if config == nil {
+			return fmt.Errorf("unable to get config from config store")
+		}
+		if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil {
+			return errors.Wrap(err, "error injecting config")
+		}
+	}
+
+	return nil
+}
+
 // getSize returns real size & virtual size
 func (daemon *Daemon) getSize(containerID string) (int64, int64) {
 	// TODO Windows

+ 19 - 10
daemon/oci_windows.go

@@ -29,6 +29,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
 		return nil, err
 	}
 
+	if err := daemon.setupConfigDir(c); err != nil {
+		return nil, err
+	}
+
 	// In s.Mounts
 	mounts, err := daemon.setupMounts(c)
 	if err != nil {
@@ -44,24 +48,25 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
 		isHyperV = c.HostConfig.Isolation.IsHyperV()
 	}
 
-	// If the container has not been started, and has secrets, create symlinks
-	// to each secret. If it has been started before, the symlinks should have
-	// already been created. Also, it is important to not mount a Hyper-V
-	// container that has been started before, to protect the host from the
-	// container; for example, from malicious mutation of NTFS data structures.
-	if !c.HasBeenStartedBefore && len(c.SecretReferences) > 0 {
+	// If the container has not been started, and has configs or secrets
+	// secrets, create symlinks to each confing and secret. If it has been
+	// started before, the symlinks should have already been created. Also, it
+	// is important to not mount a Hyper-V  container that has been started
+	// before, to protect the host from the container; for example, from
+	// malicious mutation of NTFS data structures.
+	if !c.HasBeenStartedBefore && (len(c.SecretReferences) > 0 || len(c.ConfigReferences) > 0) {
 		// The container file system is mounted before this function is called,
 		// except for Hyper-V containers, so mount it here in that case.
 		if isHyperV {
 			if err := daemon.Mount(c); err != nil {
 				return nil, err
 			}
+			defer daemon.Unmount(c)
 		}
-		err := c.CreateSecretSymlinks()
-		if isHyperV {
-			daemon.Unmount(c)
+		if err := c.CreateSecretSymlinks(); err != nil {
+			return nil, err
 		}
-		if err != nil {
+		if err := c.CreateConfigSymlinks(); err != nil {
 			return nil, err
 		}
 	}
@@ -70,6 +75,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
 		mounts = append(mounts, m...)
 	}
 
+	if m := c.ConfigMounts(); m != nil {
+		mounts = append(mounts, m...)
+	}
+
 	for _, mount := range mounts {
 		m := specs.Mount{
 			Source:      mount.Source,