Do not bind mount configs and secrets when source is a file but copy it into the container

Signed-off-by: schaubl <schaubl@users.noreply.github.com>
This commit is contained in:
schaubl 2024-07-15 17:38:51 +02:00
parent d3d378b92a
commit f54922f13f
3 changed files with 67 additions and 89 deletions

View File

@ -288,6 +288,12 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
return createConfigs{}, err return createConfigs{}, err
} }
// CONFIGS and SECRETS
err = checkContainerConfigsSecrets(*p, service)
if err != nil {
return createConfigs{}, err
}
// NETWORKING // NETWORKING
links, err := s.getLinks(ctx, p.Name, service, number) links, err := s.getLinks(ctx, p.Name, service, number)
if err != nil { if err != nil {
@ -915,128 +921,79 @@ func fillBindMounts(p types.Project, s types.ServiceConfig, m map[string]mount.M
m[bindMount.Target] = bindMount m[bindMount.Target] = bindMount
} }
secrets, err := buildContainerSecretMounts(p, s)
if err != nil {
return nil, err
}
for _, s := range secrets {
if _, found := m[s.Target]; found {
continue
}
m[s.Target] = s
}
configs, err := buildContainerConfigMounts(p, s)
if err != nil {
return nil, err
}
for _, c := range configs {
if _, found := m[c.Target]; found {
continue
}
m[c.Target] = c
}
return m, nil return m, nil
} }
func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) { func checkContainerConfigsSecrets(p types.Project, s types.ServiceConfig) error {
var mounts = map[string]mount.Mount{} err := checkContainerConfigs(p, s)
if err != nil {
return err
}
configsBaseDir := "/" err = checkContainerSecrets(p, s)
if err != nil {
return err
}
return nil
}
func checkContainerConfigs(p types.Project, s types.ServiceConfig) error {
for _, config := range s.Configs { for _, config := range s.Configs {
target := config.Target
if config.Target == "" {
target = configsBaseDir + config.Source
} else if !isAbsTarget(config.Target) {
target = configsBaseDir + config.Target
}
if config.UID != "" || config.GID != "" || config.Mode != nil { if config.UID != "" || config.GID != "" || config.Mode != nil {
logrus.Warn("config `uid`, `gid` and `mode` are not supported, they will be ignored") logrus.Warn("config `uid`, `gid` and `mode` are not supported, they will be ignored")
} }
definedConfig := p.Configs[config.Source] definedConfig := p.Configs[config.Source]
if definedConfig.External { if definedConfig.External {
return nil, fmt.Errorf("unsupported external config %s", definedConfig.Name) return fmt.Errorf("unsupported external config %s", definedConfig.Name)
} }
if definedConfig.Driver != "" { if definedConfig.Driver != "" {
return nil, errors.New("Docker Compose does not support configs.*.driver") return errors.New("Docker Compose does not support configs.*.driver")
} }
if definedConfig.TemplateDriver != "" { if definedConfig.TemplateDriver != "" {
return nil, errors.New("Docker Compose does not support configs.*.template_driver") return errors.New("Docker Compose does not support configs.*.template_driver")
} }
if definedConfig.Environment != "" || definedConfig.Content != "" { if definedConfig.File != "" {
_, err := os.Stat(definedConfig.File)
if err != nil {
return fmt.Errorf("error with source file of config %s: %s", definedConfig.Name, err.Error())
}
continue continue
} }
bindMount, err := buildMount(p, types.ServiceVolumeConfig{
Type: types.VolumeTypeBind,
Source: definedConfig.File,
Target: target,
ReadOnly: true,
})
if err != nil {
return nil, err
}
mounts[target] = bindMount
} }
values := make([]mount.Mount, 0, len(mounts)) return nil
for _, v := range mounts {
values = append(values, v)
}
return values, nil
} }
func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) { func checkContainerSecrets(p types.Project, s types.ServiceConfig) error {
var mounts = map[string]mount.Mount{}
secretsDir := "/run/secrets/"
for _, secret := range s.Secrets { for _, secret := range s.Secrets {
target := secret.Target
if secret.Target == "" {
target = secretsDir + secret.Source
} else if !isAbsTarget(secret.Target) {
target = secretsDir + secret.Target
}
if secret.UID != "" || secret.GID != "" || secret.Mode != nil { if secret.UID != "" || secret.GID != "" || secret.Mode != nil {
logrus.Warn("secrets `uid`, `gid` and `mode` are not supported, they will be ignored") logrus.Warn("secrets `uid`, `gid` and `mode` are not supported, they will be ignored")
} }
definedSecret := p.Secrets[secret.Source] definedSecret := p.Secrets[secret.Source]
if definedSecret.External { if definedSecret.External {
return nil, fmt.Errorf("unsupported external secret %s", definedSecret.Name) return fmt.Errorf("unsupported external secret %s", definedSecret.Name)
} }
if definedSecret.Driver != "" { if definedSecret.Driver != "" {
return nil, errors.New("Docker Compose does not support secrets.*.driver") return errors.New("Docker Compose does not support secrets.*.driver")
} }
if definedSecret.TemplateDriver != "" { if definedSecret.TemplateDriver != "" {
return nil, errors.New("Docker Compose does not support secrets.*.template_driver") return errors.New("Docker Compose does not support secrets.*.template_driver")
} }
if definedSecret.Environment != "" { if definedSecret.File != "" {
_, err := os.Stat(definedSecret.File)
if err != nil {
return fmt.Errorf("error with source file of secret %s: %s", definedSecret.Name, err.Error())
}
continue continue
} }
mnt, err := buildMount(p, types.ServiceVolumeConfig{
Type: types.VolumeTypeBind,
Source: definedSecret.File,
Target: target,
ReadOnly: true,
})
if err != nil {
return nil, err
}
mounts[target] = mnt
} }
values := make([]mount.Mount, 0, len(mounts)) return nil
for _, v := range mounts {
values = append(values, v)
}
return values, nil
} }
func isAbsTarget(p string) bool { func isAbsTarget(p string) bool {

View File

@ -21,6 +21,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"os"
"strconv" "strconv"
"time" "time"
@ -31,7 +32,23 @@ import (
func (s *composeService) injectSecrets(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error { func (s *composeService) injectSecrets(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
for _, config := range service.Secrets { for _, config := range service.Secrets {
file := project.Secrets[config.Source] file := project.Secrets[config.Source]
if file.Environment == "" { content := file.Content
if file.Environment != "" {
env, ok := project.Environment[file.Environment]
if !ok {
return fmt.Errorf("environment variable %q required by file %q is not set", file.Environment, file.Name)
}
content = env
} else if file.File != "" {
data, err := os.ReadFile(file.File)
if err != nil {
return err
}
content = string(data)
}
if content == "" {
continue continue
} }
@ -41,11 +58,7 @@ func (s *composeService) injectSecrets(ctx context.Context, project *types.Proje
config.Target = "/run/secrets/" + config.Target config.Target = "/run/secrets/" + config.Target
} }
env, ok := project.Environment[file.Environment] b, err := createTar(content, types.FileReferenceConfig(config))
if !ok {
return fmt.Errorf("environment variable %q required by file %q is not set", file.Environment, file.Name)
}
b, err := createTar(env, types.FileReferenceConfig(config))
if err != nil { if err != nil {
return err return err
} }
@ -64,13 +77,21 @@ func (s *composeService) injectConfigs(ctx context.Context, project *types.Proje
for _, config := range service.Configs { for _, config := range service.Configs {
file := project.Configs[config.Source] file := project.Configs[config.Source]
content := file.Content content := file.Content
if file.Environment != "" { if file.Environment != "" {
env, ok := project.Environment[file.Environment] env, ok := project.Environment[file.Environment]
if !ok { if !ok {
return fmt.Errorf("environment variable %q required by file %q is not set", file.Environment, file.Name) return fmt.Errorf("environment variable %q required by file %q is not set", file.Environment, file.Name)
} }
content = env content = env
} else if file.File != "" {
data, err := os.ReadFile(file.File)
if err != nil {
return err
}
content = string(data)
} }
if content == "" { if content == "" {
continue continue
} }

View File

@ -52,7 +52,7 @@ func TestLocalComposeVolume(t *testing.T) {
res := c.RunDockerCmd(t, "inspect", "compose-e2e-volume-nginx2-1", "--format", "{{ json .Mounts }}") res := c.RunDockerCmd(t, "inspect", "compose-e2e-volume-nginx2-1", "--format", "{{ json .Mounts }}")
output := res.Stdout() output := res.Stdout()
assert.Assert(t, strings.Contains(output, `"Destination":"/usr/src/app/node_modules","Driver":"local","Mode":"z","RW":true,"Propagation":""`), output) assert.Assert(t, strings.Contains(output, `"Destination":"/usr/src/app/node_modules","Driver":"local","Mode":"z","RW":true,"Propagation":""`), output)
assert.Assert(t, strings.Contains(output, `"Destination":"/myconfig","Mode":"","RW":false,"Propagation":"rprivate"`), output) assert.Assert(t, !strings.Contains(output, `"Destination":"/myconfig","Mode":"","RW":false,"Propagation":"rprivate"`), output)
}) })
t.Run("check config content", func(t *testing.T) { t.Run("check config content", func(t *testing.T) {