Преглед изворни кода

mutualize code from injectSecrets / injectConfigs

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof пре 2 месеци
родитељ
комит
2681ed17a7
2 измењених фајлова са 113 додато и 87 уклоњено
  1. 102 84
      pkg/compose/secrets.go
  2. 11 3
      pkg/e2e/secrets_test.go

+ 102 - 84
pkg/compose/secrets.go

@@ -29,121 +29,139 @@ import (
 	"github.com/docker/docker/api/types/container"
 )
 
+type mountType string
+
+const (
+	secretMount mountType = "secret"
+	configMount mountType = "config"
+)
+
 func (s *composeService) injectSecrets(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
-	var ctrConfig *container.Config
-	for _, config := range service.Secrets {
-		file := project.Secrets[config.Source]
-		if file.Environment == "" {
-			continue
-		}
+	return s.injectFileReferences(ctx, project, service, id, secretMount)
+}
 
-		if service.ReadOnly {
-			return fmt.Errorf("cannot create secret %q in read-only service %s: `file` is the sole supported option", file.Name, service.Name)
-		}
+func (s *composeService) injectConfigs(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
+	return s.injectFileReferences(ctx, project, service, id, configMount)
+}
 
-		if config.Target == "" {
-			config.Target = "/run/secrets/" + config.Source
-		} else if !isAbsTarget(config.Target) {
-			config.Target = "/run/secrets/" + config.Target
-		}
+func (s *composeService) injectFileReferences(ctx context.Context, project *types.Project, service types.ServiceConfig, id string, mountType mountType) error {
+	mounts, sources := s.getFilesAndMap(project, service, mountType)
+	var ctrConfig *container.Config
 
-		content := file.Content
+	for _, mount := range mounts {
+		content, err := s.resolveFileContent(project, sources[mount.Source], mountType)
+		if err != nil {
+			return err
+		}
 		if content == "" {
-			env, ok := project.Environment[file.Environment]
-			if !ok {
-				return fmt.Errorf("environment variable %q required by secret %q is not set", file.Environment, file.Name)
-			}
-			content = env
+			continue
 		}
 
-		if config.UID == "" && config.GID == "" {
-			if ctrConfig == nil {
-				ctr, err := s.apiClient().ContainerInspect(ctx, id)
-				if err != nil {
-					return err
-				}
-				ctrConfig = ctr.Config
-			}
-
-			parts := strings.Split(ctrConfig.User, ":")
-			if len(parts) > 0 {
-				config.UID = parts[0]
-			}
-			if len(parts) > 1 {
-				config.GID = parts[1]
-			}
+		if service.ReadOnly {
+			return fmt.Errorf("cannot create %s %q in read-only service %s: `file` is the sole supported option", mountType, sources[mount.Source].Name, service.Name)
 		}
 
-		b, err := createTar(content, types.FileReferenceConfig(config))
+		s.setDefaultTarget(&mount, mountType)
+
+		ctrConfig, err = s.setFileOwnership(ctx, id, &mount, ctrConfig)
 		if err != nil {
 			return err
 		}
 
-		err = s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
-			CopyUIDGID: config.UID != "" || config.GID != "",
-		})
-		if err != nil {
+		if err := s.copyFileToContainer(ctx, id, content, mount); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
-func (s *composeService) injectConfigs(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
-	var ctrConfig *container.Config
-	for _, config := range service.Configs {
-		file := project.Configs[config.Source]
-		content := file.Content
-		if file.Environment != "" {
-			env, ok := project.Environment[file.Environment]
-			if !ok {
-				return fmt.Errorf("environment variable %q required by config %q is not set", file.Environment, file.Name)
-			}
-			content = env
+func (s *composeService) getFilesAndMap(project *types.Project, service types.ServiceConfig, mountType mountType) ([]types.FileReferenceConfig, map[string]types.FileObjectConfig) {
+	var files []types.FileReferenceConfig
+	var fileMap map[string]types.FileObjectConfig
+
+	switch mountType {
+	case secretMount:
+		files = make([]types.FileReferenceConfig, len(service.Secrets))
+		for i, config := range service.Secrets {
+			files[i] = types.FileReferenceConfig(config)
 		}
-		if content == "" {
-			continue
+		fileMap = make(map[string]types.FileObjectConfig)
+		for k, v := range project.Secrets {
+			fileMap[k] = types.FileObjectConfig(v)
 		}
-
-		if service.ReadOnly {
-			return fmt.Errorf("cannot create config %q in read-only service %s: `file` is the sole supported option", file.Name, service.Name)
+	case configMount:
+		files = make([]types.FileReferenceConfig, len(service.Configs))
+		for i, config := range service.Configs {
+			files[i] = types.FileReferenceConfig(config)
 		}
-
-		if config.Target == "" {
-			config.Target = "/" + config.Source
+		fileMap = make(map[string]types.FileObjectConfig)
+		for k, v := range project.Configs {
+			fileMap[k] = types.FileObjectConfig(v)
 		}
+	}
+	return files, fileMap
+}
 
-		if config.UID == "" && config.GID == "" {
-			if ctrConfig == nil {
-				ctr, err := s.apiClient().ContainerInspect(ctx, id)
-				if err != nil {
-					return err
-				}
-				ctrConfig = ctr.Config
-			}
-
-			parts := strings.Split(ctrConfig.User, ":")
-			if len(parts) > 0 {
-				config.UID = parts[0]
-			}
-			if len(parts) > 1 {
-				config.GID = parts[1]
-			}
+func (s *composeService) resolveFileContent(project *types.Project, source types.FileObjectConfig, mountType mountType) (string, error) {
+	if source.Content != "" {
+		// inlined, or already resolved by include
+		return source.Content, nil
+	}
+	if source.Environment != "" {
+		env, ok := project.Environment[source.Environment]
+		if !ok {
+			return "", fmt.Errorf("environment variable %q required by %s %q is not set", source.Environment, mountType, source.Name)
 		}
+		return env, nil
+	}
+	return "", nil
+}
 
-		b, err := createTar(content, types.FileReferenceConfig(config))
-		if err != nil {
-			return err
+func (s *composeService) setDefaultTarget(file *types.FileReferenceConfig, mountType mountType) {
+	if file.Target == "" {
+		if mountType == secretMount {
+			file.Target = "/run/secrets/" + file.Source
+		} else {
+			file.Target = "/" + file.Source
 		}
+	} else if mountType == secretMount && !isAbsTarget(file.Target) {
+		file.Target = "/run/secrets/" + file.Target
+	}
+}
 
-		err = s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
-			CopyUIDGID: config.UID != "" || config.GID != "",
-		})
+func (s *composeService) setFileOwnership(ctx context.Context, id string, file *types.FileReferenceConfig, ctrConfig *container.Config) (*container.Config, error) {
+	if file.UID != "" || file.GID != "" {
+		return ctrConfig, nil
+	}
+
+	if ctrConfig == nil {
+		ctr, err := s.apiClient().ContainerInspect(ctx, id)
 		if err != nil {
-			return err
+			return nil, err
 		}
+		ctrConfig = ctr.Config
 	}
-	return nil
+
+	parts := strings.Split(ctrConfig.User, ":")
+	if len(parts) > 0 {
+		file.UID = parts[0]
+	}
+	if len(parts) > 1 {
+		file.GID = parts[1]
+	}
+
+	return ctrConfig, nil
+}
+
+func (s *composeService) copyFileToContainer(ctx context.Context, id, content string, file types.FileReferenceConfig) error {
+	b, err := createTar(content, file)
+	if err != nil {
+		return err
+	}
+
+	return s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
+		CopyUIDGID: true,
+	})
 }
 
 func createTar(env string, config types.FileReferenceConfig) (bytes.Buffer, error) {

+ 11 - 3
pkg/e2e/secrets_test.go

@@ -17,6 +17,7 @@
 package e2e
 
 import (
+	"strings"
 	"testing"
 
 	"gotest.tools/v3/icmd"
@@ -41,20 +42,27 @@ func TestSecretFromEnv(t *testing.T) {
 		res.Assert(t, icmd.Expected{Out: "-r--r-----    1 1005     1005"})
 	})
 	t.Run("secret uid from user", func(t *testing.T) {
-		res := icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/env-secret/compose.yaml", "run", "bar", "ls", "-al", "/var/run/secrets/bar"),
+		res := c.RunDockerCmd(t, "version", "--format", "{{ .Server.Version }}")
+		if strings.HasPrefix(res.Stdout(), "27.") {
+			t.Skip("USER uid:gid is not supported")
+		}
+		res = icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/env-secret/compose.yaml", "run", "bar", "ls", "-al", "/var/run/secrets/bar"),
 			func(cmd *icmd.Cmd) {
 				cmd.Env = append(cmd.Env, "SECRET=BAR")
 			})
 		res.Assert(t, icmd.Expected{Out: "-r--r--r--    1 1005     root"})
 	})
 	t.Run("secret uid:gid from user", func(t *testing.T) {
-		res := icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/env-secret/compose.yaml", "run", "zot", "ls", "-al", "/var/run/secrets/bar"),
+		res := c.RunDockerCmd(t, "version", "--format", "{{ .Server.Version }}")
+		if strings.HasPrefix(res.Stdout(), "27.") {
+			t.Skip("USER uid:gid is not supported")
+		}
+		res = icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/env-secret/compose.yaml", "run", "zot", "ls", "-al", "/var/run/secrets/bar"),
 			func(cmd *icmd.Cmd) {
 				cmd.Env = append(cmd.Env, "SECRET=BAR")
 			})
 		res.Assert(t, icmd.Expected{Out: "-r--r--r--    1 1005     1005"})
 	})
-
 }
 
 func TestSecretFromInclude(t *testing.T) {