Browse code

Add config support to executor backend

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2017/03/17 06:23:33
Showing 14 changed files
... ...
@@ -87,8 +87,9 @@ type CommonContainer struct {
87 87
 	MountPoints            map[string]*volume.MountPoint
88 88
 	HostConfig             *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
89 89
 	ExecCommands           *exec.Store                `json:"-"`
90
-	SecretStore            agentexec.SecretGetter     `json:"-"`
90
+	DependencyStore        agentexec.DependencyGetter `json:"-"`
91 91
 	SecretReferences       []*swarmtypes.SecretReference
92
+	ConfigReferences       []*swarmtypes.ConfigReference
92 93
 	// logDriver for closing
93 94
 	LogDriver      logger.Logger  `json:"-"`
94 95
 	LogCopier      *logger.Copier `json:"-"`
... ...
@@ -966,3 +967,14 @@ func getSecretTargetPath(r *swarmtypes.SecretReference) string {
966 966
 
967 967
 	return filepath.Join(containerSecretMountPath, r.File.Name)
968 968
 }
969
+
970
+// ConfigsDirPath returns the path to the directory where configs are stored on
971
+// disk.
972
+func (container *Container) ConfigsDirPath() string {
973
+	return filepath.Join(container.Root, "configs")
974
+}
975
+
976
+// ConfigFilePath returns the path to the on-disk location of a config.
977
+func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference) string {
978
+	return filepath.Join(container.ConfigsDirPath(), configRef.ConfigID)
979
+}
... ...
@@ -277,6 +277,23 @@ func (container *Container) UnmountSecrets() error {
277 277
 	return detachMounted(container.SecretMountPath())
278 278
 }
279 279
 
280
+// ConfigMounts returns the mounts for configs.
281
+func (container *Container) ConfigMounts() []Mount {
282
+	var mounts []Mount
283
+	for _, configRef := range container.ConfigReferences {
284
+		if configRef.File == nil {
285
+			continue
286
+		}
287
+		mounts = append(mounts, Mount{
288
+			Source:      container.ConfigFilePath(*configRef),
289
+			Destination: configRef.File.Name,
290
+			Writable:    false,
291
+		})
292
+	}
293
+
294
+	return mounts
295
+}
296
+
280 297
 // UpdateContainer updates configuration of a container.
281 298
 func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
282 299
 	container.Lock()
... ...
@@ -42,8 +42,9 @@ type Backend interface {
42 42
 	ContainerWaitWithContext(ctx context.Context, name string) error
43 43
 	ContainerRm(name string, config *types.ContainerRmConfig) error
44 44
 	ContainerKill(name string, sig uint64) error
45
-	SetContainerSecretStore(name string, store exec.SecretGetter) error
45
+	SetContainerDependencyStore(name string, store exec.DependencyGetter) error
46 46
 	SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
47
+	SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error
47 48
 	SystemInfo() (*types.Info, error)
48 49
 	VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
49 50
 	Containers(config *types.ContainerListOptions) ([]*types.Container, error)
... ...
@@ -33,21 +33,21 @@ import (
33 33
 // are mostly naked calls to the client API, seeded with information from
34 34
 // containerConfig.
35 35
 type containerAdapter struct {
36
-	backend   executorpkg.Backend
37
-	container *containerConfig
38
-	secrets   exec.SecretGetter
36
+	backend      executorpkg.Backend
37
+	container    *containerConfig
38
+	dependencies exec.DependencyGetter
39 39
 }
40 40
 
41
-func newContainerAdapter(b executorpkg.Backend, task *api.Task, secrets exec.SecretGetter) (*containerAdapter, error) {
41
+func newContainerAdapter(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*containerAdapter, error) {
42 42
 	ctnr, err := newContainerConfig(task)
43 43
 	if err != nil {
44 44
 		return nil, err
45 45
 	}
46 46
 
47 47
 	return &containerAdapter{
48
-		container: ctnr,
49
-		backend:   b,
50
-		secrets:   secrets,
48
+		container:    ctnr,
49
+		backend:      b,
50
+		dependencies: dependencies,
51 51
 	}, nil
52 52
 }
53 53
 
... ...
@@ -243,13 +243,18 @@ func (c *containerAdapter) create(ctx context.Context) error {
243 243
 		return errors.New("unable to get container from task spec")
244 244
 	}
245 245
 
246
+	if err := c.backend.SetContainerDependencyStore(cr.ID, c.dependencies); err != nil {
247
+		return err
248
+	}
249
+
246 250
 	// configure secrets
247
-	if err := c.backend.SetContainerSecretStore(cr.ID, c.secrets); err != nil {
251
+	secretRefs := convert.SecretReferencesFromGRPC(container.Secrets)
252
+	if err := c.backend.SetContainerSecretReferences(cr.ID, secretRefs); err != nil {
248 253
 		return err
249 254
 	}
250 255
 
251
-	refs := convert.SecretReferencesFromGRPC(container.Secrets)
252
-	if err := c.backend.SetContainerSecretReferences(cr.ID, refs); err != nil {
256
+	configRefs := convert.ConfigReferencesFromGRPC(container.Configs)
257
+	if err := c.backend.SetContainerConfigReferences(cr.ID, configRefs); err != nil {
253 258
 		return err
254 259
 	}
255 260
 
... ...
@@ -20,8 +20,8 @@ type networkAttacherController struct {
20 20
 	closed  chan struct{}
21 21
 }
22 22
 
23
-func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, secrets exec.SecretGetter) (*networkAttacherController, error) {
24
-	adapter, err := newContainerAdapter(b, task, secrets)
23
+func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
24
+	adapter, err := newContainerAdapter(b, task, dependencies)
25 25
 	if err != nil {
26 26
 		return nil, err
27 27
 	}
... ...
@@ -39,8 +39,8 @@ type controller struct {
39 39
 var _ exec.Controller = &controller{}
40 40
 
41 41
 // NewController returns a docker exec runner for the provided task.
42
-func newController(b executorpkg.Backend, task *api.Task, secrets exec.SecretGetter) (*controller, error) {
43
-	adapter, err := newContainerAdapter(b, task, secrets)
42
+func newController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*controller, error) {
43
+	adapter, err := newContainerAdapter(b, task, dependencies)
44 44
 	if err != nil {
45 45
 		return nil, err
46 46
 	}
... ...
@@ -14,23 +14,23 @@ import (
14 14
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
15 15
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
16 16
 	networktypes "github.com/docker/libnetwork/types"
17
+	"github.com/docker/swarmkit/agent"
17 18
 	"github.com/docker/swarmkit/agent/exec"
18
-	"github.com/docker/swarmkit/agent/secrets"
19 19
 	"github.com/docker/swarmkit/api"
20 20
 	"github.com/docker/swarmkit/api/naming"
21 21
 	"golang.org/x/net/context"
22 22
 )
23 23
 
24 24
 type executor struct {
25
-	backend executorpkg.Backend
26
-	secrets exec.SecretsManager
25
+	backend      executorpkg.Backend
26
+	dependencies exec.DependencyManager
27 27
 }
28 28
 
29 29
 // NewExecutor returns an executor from the docker client.
30 30
 func NewExecutor(b executorpkg.Backend) exec.Executor {
31 31
 	return &executor{
32
-		backend: b,
33
-		secrets: secrets.NewManager(),
32
+		backend:      b,
33
+		dependencies: agent.NewDependencyManager(),
34 34
 	}
35 35
 }
36 36
 
... ...
@@ -162,8 +162,10 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error {
162 162
 
163 163
 // Controller returns a docker container runner.
164 164
 func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
165
+	dependencyGetter := agent.Restrict(e.dependencies, t)
166
+
165 167
 	if t.Spec.GetAttachment() != nil {
166
-		return newNetworkAttacherController(e.backend, t, e.secrets)
168
+		return newNetworkAttacherController(e.backend, t, dependencyGetter)
167 169
 	}
168 170
 
169 171
 	var ctlr exec.Controller
... ...
@@ -188,7 +190,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
188 188
 			return ctlr, fmt.Errorf("unsupported runtime type: %q", r.Generic.Kind)
189 189
 		}
190 190
 	case *api.TaskSpec_Container:
191
-		c, err := newController(e.backend, t, secrets.Restrict(e.secrets, t))
191
+		c, err := newController(e.backend, t, dependencyGetter)
192 192
 		if err != nil {
193 193
 			return ctlr, err
194 194
 		}
... ...
@@ -218,7 +220,11 @@ func (e *executor) SetNetworkBootstrapKeys(keys []*api.EncryptionKey) error {
218 218
 }
219 219
 
220 220
 func (e *executor) Secrets() exec.SecretsManager {
221
-	return e.secrets
221
+	return e.dependencies.Secrets()
222
+}
223
+
224
+func (e *executor) Configs() exec.ConfigsManager {
225
+	return e.dependencies.Configs()
222 226
 }
223 227
 
224 228
 type sortedPlugins []api.PluginDescription
225 229
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package daemon
1
+
2
+import (
3
+	"github.com/Sirupsen/logrus"
4
+	swarmtypes "github.com/docker/docker/api/types/swarm"
5
+)
6
+
7
+// SetContainerConfigReferences sets the container config references needed
8
+func (daemon *Daemon) SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error {
9
+	if !configsSupported() && len(refs) > 0 {
10
+		logrus.Warn("configs are not supported on this platform")
11
+		return nil
12
+	}
13
+
14
+	c, err := daemon.GetContainer(name)
15
+	if err != nil {
16
+		return err
17
+	}
18
+
19
+	c.ConfigReferences = refs
20
+
21
+	return nil
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build linux
1
+
2
+package daemon
3
+
4
+func configsSupported() bool {
5
+	return true
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !linux
1
+
2
+package daemon
3
+
4
+func configsSupported() bool {
5
+	return false
6
+}
... ...
@@ -145,6 +145,13 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
145 145
 	localMountPath := c.SecretMountPath()
146 146
 	logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
147 147
 
148
+	// retrieve possible remapped range start for root UID, GID
149
+	rootUID, rootGID := daemon.GetRemappedUIDGID()
150
+	// create tmpfs
151
+	if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
152
+		return errors.Wrap(err, "error creating secret local mount path")
153
+	}
154
+
148 155
 	defer func() {
149 156
 		if setupErr != nil {
150 157
 			// cleanup
... ...
@@ -156,25 +163,20 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
156 156
 		}
157 157
 	}()
158 158
 
159
-	// retrieve possible remapped range start for root UID, GID
160
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
161
-	// create tmpfs
162
-	if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
163
-		return errors.Wrap(err, "error creating secret local mount path")
164
-	}
165 159
 	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootUID, rootGID)
166 160
 	if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
167 161
 		return errors.Wrap(err, "unable to setup secret mount")
168 162
 	}
169 163
 
170
-	for _, s := range c.SecretReferences {
171
-		if c.SecretStore == nil {
172
-			return fmt.Errorf("secret store is not initialized")
173
-		}
164
+	if c.DependencyStore == nil {
165
+		return fmt.Errorf("secret store is not initialized")
166
+	}
174 167
 
168
+	for _, s := range c.SecretReferences {
175 169
 		// TODO (ehazlett): use type switch when more are supported
176 170
 		if s.File == nil {
177
-			return fmt.Errorf("secret target type is not a file target")
171
+			logrus.Error("secret target type is not a file target")
172
+			continue
178 173
 		}
179 174
 
180 175
 		// secrets are created in the SecretMountPath on the host, at a
... ...
@@ -188,7 +190,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
188 188
 			"name": s.File.Name,
189 189
 			"path": fPath,
190 190
 		}).Debug("injecting secret")
191
-		secret := c.SecretStore.Get(s.SecretID)
191
+		secret := c.DependencyStore.Secrets().Get(s.SecretID)
192 192
 		if secret == nil {
193 193
 			return fmt.Errorf("unable to get secret from secret store")
194 194
 		}
... ...
@@ -220,6 +222,74 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
220 220
 	return nil
221 221
 }
222 222
 
223
+func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
224
+	if len(c.ConfigReferences) == 0 {
225
+		return nil
226
+	}
227
+
228
+	localPath := c.ConfigsDirPath()
229
+	logrus.Debugf("configs: setting up config dir: %s", localPath)
230
+
231
+	// retrieve possible remapped range start for root UID, GID
232
+	rootUID, rootGID := daemon.GetRemappedUIDGID()
233
+	// create tmpfs
234
+	if err := idtools.MkdirAllAs(localPath, 0700, rootUID, rootGID); err != nil {
235
+		return errors.Wrap(err, "error creating config dir")
236
+	}
237
+
238
+	defer func() {
239
+		if setupErr != nil {
240
+			if err := os.RemoveAll(localPath); err != nil {
241
+				logrus.Errorf("error cleaning up config dir: %s", err)
242
+			}
243
+		}
244
+	}()
245
+
246
+	if c.DependencyStore == nil {
247
+		return fmt.Errorf("config store is not initialized")
248
+	}
249
+
250
+	for _, configRef := range c.ConfigReferences {
251
+		// TODO (ehazlett): use type switch when more are supported
252
+		if configRef.File == nil {
253
+			logrus.Error("config target type is not a file target")
254
+			continue
255
+		}
256
+
257
+		fPath := c.ConfigFilePath(*configRef)
258
+
259
+		log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath})
260
+
261
+		if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
262
+			return errors.Wrap(err, "error creating config path")
263
+		}
264
+
265
+		log.Debug("injecting config")
266
+		config := c.DependencyStore.Configs().Get(configRef.ConfigID)
267
+		if config == nil {
268
+			return fmt.Errorf("unable to get config from config store")
269
+		}
270
+		if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil {
271
+			return errors.Wrap(err, "error injecting config")
272
+		}
273
+
274
+		uid, err := strconv.Atoi(configRef.File.UID)
275
+		if err != nil {
276
+			return err
277
+		}
278
+		gid, err := strconv.Atoi(configRef.File.GID)
279
+		if err != nil {
280
+			return err
281
+		}
282
+
283
+		if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
284
+			return errors.Wrap(err, "error setting ownership for config")
285
+		}
286
+	}
287
+
288
+	return nil
289
+}
290
+
223 291
 func killProcessDirectly(container *container.Container) error {
224 292
 	if _, err := container.WaitStop(10 * time.Second); err != nil {
225 293
 		// Ensure that we don't kill ourselves
226 294
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+package daemon
1
+
2
+import (
3
+	"github.com/docker/swarmkit/agent/exec"
4
+)
5
+
6
+// SetContainerDependencyStore sets the dependency store backend for the container
7
+func (daemon *Daemon) SetContainerDependencyStore(name string, store exec.DependencyGetter) error {
8
+	c, err := daemon.GetContainer(name)
9
+	if err != nil {
10
+		return err
11
+	}
12
+
13
+	c.DependencyStore = store
14
+
15
+	return nil
16
+}
... ...
@@ -737,6 +737,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
737 737
 		return nil, err
738 738
 	}
739 739
 
740
+	if err := daemon.setupConfigDir(c); err != nil {
741
+		return nil, err
742
+	}
743
+
740 744
 	ms, err := daemon.setupMounts(c)
741 745
 	if err != nil {
742 746
 		return nil, err
... ...
@@ -754,6 +758,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
754 754
 		ms = append(ms, m...)
755 755
 	}
756 756
 
757
+	ms = append(ms, c.ConfigMounts()...)
758
+
757 759
 	sort.Sort(mounts(ms))
758 760
 	if err := setMounts(daemon, &s, c, ms); err != nil {
759 761
 		return nil, fmt.Errorf("linux mounts: %v", err)
... ...
@@ -3,21 +3,8 @@ package daemon
3 3
 import (
4 4
 	"github.com/Sirupsen/logrus"
5 5
 	swarmtypes "github.com/docker/docker/api/types/swarm"
6
-	"github.com/docker/swarmkit/agent/exec"
7 6
 )
8 7
 
9
-// SetContainerSecretStore sets the secret store backend for the container
10
-func (daemon *Daemon) SetContainerSecretStore(name string, store exec.SecretGetter) error {
11
-	c, err := daemon.GetContainer(name)
12
-	if err != nil {
13
-		return err
14
-	}
15
-
16
-	c.SecretStore = store
17
-
18
-	return nil
19
-}
20
-
21 8
 // SetContainerSecretReferences sets the container secret references needed
22 9
 func (daemon *Daemon) SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error {
23 10
 	if !secretsSupported() && len(refs) > 0 {