Browse code

Add Windows configs support

Signed-off-by: John Stephens <johnstep@docker.com>

John Stephens authored on 2017/05/12 03:55:03
Showing 5 changed files
... ...
@@ -14,6 +14,7 @@ import (
14 14
 const (
15 15
 	containerSecretMountPath         = `C:\ProgramData\Docker\secrets`
16 16
 	containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets`
17
+	containerInternalConfigsDirPath  = `C:\ProgramData\Docker\internal\configs`
17 18
 )
18 19
 
19 20
 // Container holds fields specific to the Windows implementation. See
... ...
@@ -91,6 +92,43 @@ func (container *Container) UnmountSecrets() error {
91 91
 	return os.RemoveAll(container.SecretMountPath())
92 92
 }
93 93
 
94
+// CreateConfigSymlinks creates symlinks to files in the config mount.
95
+func (container *Container) CreateConfigSymlinks() error {
96
+	for _, configRef := range container.ConfigReferences {
97
+		if configRef.File == nil {
98
+			continue
99
+		}
100
+		resolvedPath, _, err := container.ResolvePath(configRef.File.Name)
101
+		if err != nil {
102
+			return err
103
+		}
104
+		if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil {
105
+			return err
106
+		}
107
+		if err := os.Symlink(filepath.Join(containerInternalConfigsDirPath, configRef.ConfigID), resolvedPath); err != nil {
108
+			return err
109
+		}
110
+	}
111
+
112
+	return nil
113
+}
114
+
115
+// ConfigMounts returns the mount for configs.
116
+// All configs are stored in a single mount on Windows. Target symlinks are
117
+// created for each config, pointing to the files in this mount.
118
+func (container *Container) ConfigMounts() []Mount {
119
+	var mounts []Mount
120
+	if len(container.ConfigReferences) > 0 {
121
+		mounts = append(mounts, Mount{
122
+			Source:      container.ConfigsDirPath(),
123
+			Destination: containerInternalConfigsDirPath,
124
+			Writable:    false,
125
+		})
126
+	}
127
+
128
+	return mounts
129
+}
130
+
94 131
 // DetachAndUnmount unmounts all volumes.
95 132
 // On Windows it only delegates to `UnmountVolumes` since there is nothing to
96 133
 // force unmount.
... ...
@@ -1,4 +1,4 @@
1
-// +build !linux
1
+// +build !linux,!windows
2 2
 
3 3
 package daemon
4 4
 
5 5
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build windows
1
+
2
+package daemon
3
+
4
+func configsSupported() bool {
5
+	return true
6
+}
... ...
@@ -16,6 +16,55 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s
16 16
 	return nil, nil
17 17
 }
18 18
 
19
+func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
20
+	if len(c.ConfigReferences) == 0 {
21
+		return nil
22
+	}
23
+
24
+	localPath := c.ConfigsDirPath()
25
+	logrus.Debugf("configs: setting up config dir: %s", localPath)
26
+
27
+	// create local config root
28
+	if err := system.MkdirAllWithACL(localPath, 0); err != nil {
29
+		return errors.Wrap(err, "error creating config dir")
30
+	}
31
+
32
+	defer func() {
33
+		if setupErr != nil {
34
+			if err := os.RemoveAll(localPath); err != nil {
35
+				logrus.Errorf("error cleaning up config dir: %s", err)
36
+			}
37
+		}
38
+	}()
39
+
40
+	if c.DependencyStore == nil {
41
+		return fmt.Errorf("config store is not initialized")
42
+	}
43
+
44
+	for _, configRef := range c.ConfigReferences {
45
+		// TODO (ehazlett): use type switch when more are supported
46
+		if configRef.File == nil {
47
+			logrus.Error("config target type is not a file target")
48
+			continue
49
+		}
50
+
51
+		fPath := c.ConfigFilePath(*configRef)
52
+
53
+		log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath})
54
+
55
+		log.Debug("injecting config")
56
+		config := c.DependencyStore.Configs().Get(configRef.ConfigID)
57
+		if config == nil {
58
+			return fmt.Errorf("unable to get config from config store")
59
+		}
60
+		if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil {
61
+			return errors.Wrap(err, "error injecting config")
62
+		}
63
+	}
64
+
65
+	return nil
66
+}
67
+
19 68
 // getSize returns real size & virtual size
20 69
 func (daemon *Daemon) getSize(containerID string) (int64, int64) {
21 70
 	// TODO Windows
... ...
@@ -29,6 +29,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
29 29
 		return nil, err
30 30
 	}
31 31
 
32
+	if err := daemon.setupConfigDir(c); err != nil {
33
+		return nil, err
34
+	}
35
+
32 36
 	// In s.Mounts
33 37
 	mounts, err := daemon.setupMounts(c)
34 38
 	if err != nil {
... ...
@@ -44,24 +48,25 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
44 44
 		isHyperV = c.HostConfig.Isolation.IsHyperV()
45 45
 	}
46 46
 
47
-	// If the container has not been started, and has secrets, create symlinks
48
-	// to each secret. If it has been started before, the symlinks should have
49
-	// already been created. Also, it is important to not mount a Hyper-V
50
-	// container that has been started before, to protect the host from the
51
-	// container; for example, from malicious mutation of NTFS data structures.
52
-	if !c.HasBeenStartedBefore && len(c.SecretReferences) > 0 {
47
+	// If the container has not been started, and has configs or secrets
48
+	// secrets, create symlinks to each confing and secret. If it has been
49
+	// started before, the symlinks should have already been created. Also, it
50
+	// is important to not mount a Hyper-V  container that has been started
51
+	// before, to protect the host from the container; for example, from
52
+	// malicious mutation of NTFS data structures.
53
+	if !c.HasBeenStartedBefore && (len(c.SecretReferences) > 0 || len(c.ConfigReferences) > 0) {
53 54
 		// The container file system is mounted before this function is called,
54 55
 		// except for Hyper-V containers, so mount it here in that case.
55 56
 		if isHyperV {
56 57
 			if err := daemon.Mount(c); err != nil {
57 58
 				return nil, err
58 59
 			}
60
+			defer daemon.Unmount(c)
59 61
 		}
60
-		err := c.CreateSecretSymlinks()
61
-		if isHyperV {
62
-			daemon.Unmount(c)
62
+		if err := c.CreateSecretSymlinks(); err != nil {
63
+			return nil, err
63 64
 		}
64
-		if err != nil {
65
+		if err := c.CreateConfigSymlinks(); err != nil {
65 66
 			return nil, err
66 67
 		}
67 68
 	}
... ...
@@ -70,6 +75,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
70 70
 		mounts = append(mounts, m...)
71 71
 	}
72 72
 
73
+	if m := c.ConfigMounts(); m != nil {
74
+		mounts = append(mounts, m...)
75
+	}
76
+
73 77
 	for _, mount := range mounts {
74 78
 		m := specs.Mount{
75 79
 			Source:      mount.Source,