Add config support to executor backend

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2017-03-16 14:23:33 -07:00
parent a58cc35ab8
commit 9e9fc7b57c
14 changed files with 208 additions and 50 deletions

View File

@ -87,8 +87,9 @@ type CommonContainer struct {
MountPoints map[string]*volume.MountPoint
HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
ExecCommands *exec.Store `json:"-"`
SecretStore agentexec.SecretGetter `json:"-"`
DependencyStore agentexec.DependencyGetter `json:"-"`
SecretReferences []*swarmtypes.SecretReference
ConfigReferences []*swarmtypes.ConfigReference
// logDriver for closing
LogDriver logger.Logger `json:"-"`
LogCopier *logger.Copier `json:"-"`
@ -966,3 +967,14 @@ func getSecretTargetPath(r *swarmtypes.SecretReference) string {
return filepath.Join(containerSecretMountPath, r.File.Name)
}
// ConfigsDirPath returns the path to the directory where configs are stored on
// disk.
func (container *Container) ConfigsDirPath() string {
return filepath.Join(container.Root, "configs")
}
// ConfigFilePath returns the path to the on-disk location of a config.
func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference) string {
return filepath.Join(container.ConfigsDirPath(), configRef.ConfigID)
}

View File

@ -277,6 +277,23 @@ func (container *Container) UnmountSecrets() error {
return detachMounted(container.SecretMountPath())
}
// ConfigMounts returns the mounts for configs.
func (container *Container) ConfigMounts() []Mount {
var mounts []Mount
for _, configRef := range container.ConfigReferences {
if configRef.File == nil {
continue
}
mounts = append(mounts, Mount{
Source: container.ConfigFilePath(*configRef),
Destination: configRef.File.Name,
Writable: false,
})
}
return mounts
}
// UpdateContainer updates configuration of a container.
func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
container.Lock()

View File

@ -42,8 +42,9 @@ type Backend interface {
ContainerWaitWithContext(ctx context.Context, name string) error
ContainerRm(name string, config *types.ContainerRmConfig) error
ContainerKill(name string, sig uint64) error
SetContainerSecretStore(name string, store exec.SecretGetter) error
SetContainerDependencyStore(name string, store exec.DependencyGetter) error
SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error
SystemInfo() (*types.Info, error)
VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
Containers(config *types.ContainerListOptions) ([]*types.Container, error)

View File

@ -33,21 +33,21 @@ import (
// are mostly naked calls to the client API, seeded with information from
// containerConfig.
type containerAdapter struct {
backend executorpkg.Backend
container *containerConfig
secrets exec.SecretGetter
backend executorpkg.Backend
container *containerConfig
dependencies exec.DependencyGetter
}
func newContainerAdapter(b executorpkg.Backend, task *api.Task, secrets exec.SecretGetter) (*containerAdapter, error) {
func newContainerAdapter(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*containerAdapter, error) {
ctnr, err := newContainerConfig(task)
if err != nil {
return nil, err
}
return &containerAdapter{
container: ctnr,
backend: b,
secrets: secrets,
container: ctnr,
backend: b,
dependencies: dependencies,
}, nil
}
@ -243,13 +243,18 @@ func (c *containerAdapter) create(ctx context.Context) error {
return errors.New("unable to get container from task spec")
}
// configure secrets
if err := c.backend.SetContainerSecretStore(cr.ID, c.secrets); err != nil {
if err := c.backend.SetContainerDependencyStore(cr.ID, c.dependencies); err != nil {
return err
}
refs := convert.SecretReferencesFromGRPC(container.Secrets)
if err := c.backend.SetContainerSecretReferences(cr.ID, refs); err != nil {
// configure secrets
secretRefs := convert.SecretReferencesFromGRPC(container.Secrets)
if err := c.backend.SetContainerSecretReferences(cr.ID, secretRefs); err != nil {
return err
}
configRefs := convert.ConfigReferencesFromGRPC(container.Configs)
if err := c.backend.SetContainerConfigReferences(cr.ID, configRefs); err != nil {
return err
}

View File

@ -20,8 +20,8 @@ type networkAttacherController struct {
closed chan struct{}
}
func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, secrets exec.SecretGetter) (*networkAttacherController, error) {
adapter, err := newContainerAdapter(b, task, secrets)
func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
adapter, err := newContainerAdapter(b, task, dependencies)
if err != nil {
return nil, err
}

View File

@ -39,8 +39,8 @@ type controller struct {
var _ exec.Controller = &controller{}
// NewController returns a docker exec runner for the provided task.
func newController(b executorpkg.Backend, task *api.Task, secrets exec.SecretGetter) (*controller, error) {
adapter, err := newContainerAdapter(b, task, secrets)
func newController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*controller, error) {
adapter, err := newContainerAdapter(b, task, dependencies)
if err != nil {
return nil, err
}

View File

@ -14,23 +14,23 @@ import (
executorpkg "github.com/docker/docker/daemon/cluster/executor"
clustertypes "github.com/docker/docker/daemon/cluster/provider"
networktypes "github.com/docker/libnetwork/types"
"github.com/docker/swarmkit/agent"
"github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/agent/secrets"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/api/naming"
"golang.org/x/net/context"
)
type executor struct {
backend executorpkg.Backend
secrets exec.SecretsManager
backend executorpkg.Backend
dependencies exec.DependencyManager
}
// NewExecutor returns an executor from the docker client.
func NewExecutor(b executorpkg.Backend) exec.Executor {
return &executor{
backend: b,
secrets: secrets.NewManager(),
backend: b,
dependencies: agent.NewDependencyManager(),
}
}
@ -162,8 +162,10 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error {
// Controller returns a docker container runner.
func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
dependencyGetter := agent.Restrict(e.dependencies, t)
if t.Spec.GetAttachment() != nil {
return newNetworkAttacherController(e.backend, t, e.secrets)
return newNetworkAttacherController(e.backend, t, dependencyGetter)
}
var ctlr exec.Controller
@ -188,7 +190,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
return ctlr, fmt.Errorf("unsupported runtime type: %q", r.Generic.Kind)
}
case *api.TaskSpec_Container:
c, err := newController(e.backend, t, secrets.Restrict(e.secrets, t))
c, err := newController(e.backend, t, dependencyGetter)
if err != nil {
return ctlr, err
}
@ -218,7 +220,11 @@ func (e *executor) SetNetworkBootstrapKeys(keys []*api.EncryptionKey) error {
}
func (e *executor) Secrets() exec.SecretsManager {
return e.secrets
return e.dependencies.Secrets()
}
func (e *executor) Configs() exec.ConfigsManager {
return e.dependencies.Configs()
}
type sortedPlugins []api.PluginDescription

23
daemon/configs.go Normal file
View File

@ -0,0 +1,23 @@
package daemon
import (
"github.com/Sirupsen/logrus"
swarmtypes "github.com/docker/docker/api/types/swarm"
)
// SetContainerConfigReferences sets the container config references needed
func (daemon *Daemon) SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error {
if !configsSupported() && len(refs) > 0 {
logrus.Warn("configs are not supported on this platform")
return nil
}
c, err := daemon.GetContainer(name)
if err != nil {
return err
}
c.ConfigReferences = refs
return nil
}

7
daemon/configs_linux.go Normal file
View File

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

View File

@ -0,0 +1,7 @@
// +build !linux
package daemon
func configsSupported() bool {
return false
}

View File

@ -145,6 +145,13 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
localMountPath := c.SecretMountPath()
logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
// retrieve possible remapped range start for root UID, GID
rootUID, rootGID := daemon.GetRemappedUIDGID()
// create tmpfs
if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
return errors.Wrap(err, "error creating secret local mount path")
}
defer func() {
if setupErr != nil {
// cleanup
@ -156,25 +163,20 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
}
}()
// retrieve possible remapped range start for root UID, GID
rootUID, rootGID := daemon.GetRemappedUIDGID()
// create tmpfs
if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
return errors.Wrap(err, "error creating secret local mount path")
}
tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootUID, rootGID)
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
return errors.Wrap(err, "unable to setup secret mount")
}
for _, s := range c.SecretReferences {
if c.SecretStore == nil {
return fmt.Errorf("secret store is not initialized")
}
if c.DependencyStore == nil {
return fmt.Errorf("secret store is not initialized")
}
for _, s := range c.SecretReferences {
// TODO (ehazlett): use type switch when more are supported
if s.File == nil {
return fmt.Errorf("secret target type is not a file target")
logrus.Error("secret target type is not a file target")
continue
}
// secrets are created in the SecretMountPath on the host, at a
@ -188,7 +190,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
"name": s.File.Name,
"path": fPath,
}).Debug("injecting secret")
secret := c.SecretStore.Get(s.SecretID)
secret := c.DependencyStore.Secrets().Get(s.SecretID)
if secret == nil {
return fmt.Errorf("unable to get secret from secret store")
}
@ -220,6 +222,74 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
return 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)
// retrieve possible remapped range start for root UID, GID
rootUID, rootGID := daemon.GetRemappedUIDGID()
// create tmpfs
if err := idtools.MkdirAllAs(localPath, 0700, rootUID, rootGID); 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})
if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
return errors.Wrap(err, "error creating config path")
}
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")
}
uid, err := strconv.Atoi(configRef.File.UID)
if err != nil {
return err
}
gid, err := strconv.Atoi(configRef.File.GID)
if err != nil {
return err
}
if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
return errors.Wrap(err, "error setting ownership for config")
}
}
return nil
}
func killProcessDirectly(container *container.Container) error {
if _, err := container.WaitStop(10 * time.Second); err != nil {
// Ensure that we don't kill ourselves

17
daemon/dependency.go Normal file
View File

@ -0,0 +1,17 @@
package daemon
import (
"github.com/docker/swarmkit/agent/exec"
)
// SetContainerDependencyStore sets the dependency store backend for the container
func (daemon *Daemon) SetContainerDependencyStore(name string, store exec.DependencyGetter) error {
c, err := daemon.GetContainer(name)
if err != nil {
return err
}
c.DependencyStore = store
return nil
}

View File

@ -737,6 +737,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
return nil, err
}
if err := daemon.setupConfigDir(c); err != nil {
return nil, err
}
ms, err := daemon.setupMounts(c)
if err != nil {
return nil, err
@ -754,6 +758,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
ms = append(ms, m...)
}
ms = append(ms, c.ConfigMounts()...)
sort.Sort(mounts(ms))
if err := setMounts(daemon, &s, c, ms); err != nil {
return nil, fmt.Errorf("linux mounts: %v", err)

View File

@ -3,21 +3,8 @@ package daemon
import (
"github.com/Sirupsen/logrus"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/swarmkit/agent/exec"
)
// SetContainerSecretStore sets the secret store backend for the container
func (daemon *Daemon) SetContainerSecretStore(name string, store exec.SecretGetter) error {
c, err := daemon.GetContainer(name)
if err != nil {
return err
}
c.SecretStore = store
return nil
}
// SetContainerSecretReferences sets the container secret references needed
func (daemon *Daemon) SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error {
if !secretsSupported() && len(refs) > 0 {