@@ -29,121 +29,139 @@ import (
2929 "github.com/docker/docker/api/types/container"
3030)
3131
32+ type mountType string
33+
34+ const (
35+ secretMount mountType = "secret"
36+ configMount mountType = "config"
37+ )
38+
3239func (s *composeService) injectSecrets(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
33- var ctrConfig *container.Config
34- for _, config := range service.Secrets {
35- file := project.Secrets[config.Source]
36- if file.Environment == "" {
37- continue
38- }
40+ return s.injectFileReferences(ctx, project, service, id, secretMount)
41+ }
3942
40- if service.ReadOnly {
41- return fmt.Errorf("cannot create secret %q in read-only service %s: `file` is the sole supported option", file.Name, service.Name )
42- }
43+ func (s *composeService) injectConfigs(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
44+ return s.injectFileReferences(ctx, project, service, id, configMount )
45+ }
4346
44- if config.Target == "" {
45- config.Target = "/run/secrets/" + config.Source
46- } else if !isAbsTarget(config.Target) {
47- config.Target = "/run/secrets/" + config.Target
48- }
47+ func (s *composeService) injectFileReferences(ctx context.Context, project *types.Project, service types.ServiceConfig, id string, mountType mountType) error {
48+ mounts, sources := s.getFilesAndMap(project, service, mountType)
49+ var ctrConfig *container.Config
4950
50- content := file.Content
51+ for _, mount := range mounts {
52+ content, err := s.resolveFileContent(project, sources[mount.Source], mountType)
53+ if err != nil {
54+ return err
55+ }
5156 if content == "" {
52- env, ok := project.Environment[file.Environment]
53- if !ok {
54- return fmt.Errorf("environment variable %q required by secret %q is not set", file.Environment, file.Name)
55- }
56- content = env
57+ continue
5758 }
5859
59- if config.UID == "" && config.GID == "" {
60- if ctrConfig == nil {
61- ctr, err := s.apiClient().ContainerInspect(ctx, id)
62- if err != nil {
63- return err
64- }
65- ctrConfig = ctr.Config
66- }
67-
68- parts := strings.Split(ctrConfig.User, ":")
69- if len(parts) > 0 {
70- config.UID = parts[0]
71- }
72- if len(parts) > 1 {
73- config.GID = parts[1]
74- }
60+ if service.ReadOnly {
61+ 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)
7562 }
7663
77- b, err := createTar(content, types.FileReferenceConfig(config))
64+ s.setDefaultTarget(&mount, mountType)
65+
66+ ctrConfig, err = s.setFileOwnership(ctx, id, &mount, ctrConfig)
7867 if err != nil {
7968 return err
8069 }
8170
82- err = s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
83- CopyUIDGID: config.UID != "" || config.GID != "",
84- })
85- if err != nil {
71+ if err := s.copyFileToContainer(ctx, id, content, mount); err != nil {
8672 return err
8773 }
8874 }
8975 return nil
9076}
9177
92- func (s *composeService) injectConfigs(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
93- var ctrConfig *container.Config
94- for _, config := range service.Configs {
95- file := project.Configs[config.Source]
96- content := file.Content
97- if file.Environment != "" {
98- env, ok := project.Environment[file.Environment]
99- if !ok {
100- return fmt.Errorf("environment variable %q required by config %q is not set", file.Environment, file.Name)
101- }
102- content = env
78+ func (s *composeService) getFilesAndMap(project *types.Project, service types.ServiceConfig, mountType mountType) ([]types.FileReferenceConfig, map[string]types.FileObjectConfig) {
79+ var files []types.FileReferenceConfig
80+ var fileMap map[string]types.FileObjectConfig
81+
82+ switch mountType {
83+ case secretMount:
84+ files = make([]types.FileReferenceConfig, len(service.Secrets))
85+ for i, config := range service.Secrets {
86+ files[i] = types.FileReferenceConfig(config)
10387 }
104- if content == "" {
105- continue
88+ fileMap = make(map[string]types.FileObjectConfig)
89+ for k, v := range project.Secrets {
90+ fileMap[k] = types.FileObjectConfig(v)
10691 }
107-
108- if service.ReadOnly {
109- return fmt.Errorf("cannot create config %q in read-only service %s: `file` is the sole supported option", file.Name, service.Name)
92+ case configMount:
93+ files = make([]types.FileReferenceConfig, len(service.Configs))
94+ for i, config := range service.Configs {
95+ files[i] = types.FileReferenceConfig(config)
11096 }
111-
112- if config.Target == "" {
113- config.Target = "/" + config.Source
97+ fileMap = make(map[string]types.FileObjectConfig)
98+ for k, v := range project.Configs {
99+ fileMap[k] = types.FileObjectConfig(v)
114100 }
101+ }
102+ return files, fileMap
103+ }
115104
116- if config.UID == "" && config.GID == "" {
117- if ctrConfig == nil {
118- ctr, err := s.apiClient().ContainerInspect(ctx, id)
119- if err != nil {
120- return err
121- }
122- ctrConfig = ctr.Config
123- }
124-
125- parts := strings.Split(ctrConfig.User, ":")
126- if len(parts) > 0 {
127- config.UID = parts[0]
128- }
129- if len(parts) > 1 {
130- config.GID = parts[1]
131- }
105+ func (s *composeService) resolveFileContent(project *types.Project, source types.FileObjectConfig, mountType mountType) (string, error) {
106+ if source.Content != "" {
107+ // inlined, or already resolved by include
108+ return source.Content, nil
109+ }
110+ if source.Environment != "" {
111+ env, ok := project.Environment[source.Environment]
112+ if !ok {
113+ return "", fmt.Errorf("environment variable %q required by %s %q is not set", source.Environment, mountType, source.Name)
132114 }
115+ return env, nil
116+ }
117+ return "", nil
118+ }
133119
134- b, err := createTar(content, types.FileReferenceConfig(config))
135- if err != nil {
136- return err
120+ func (s *composeService) setDefaultTarget(file *types.FileReferenceConfig, mountType mountType) {
121+ if file.Target == "" {
122+ if mountType == secretMount {
123+ file.Target = "/run/secrets/" + file.Source
124+ } else {
125+ file.Target = "/" + file.Source
137126 }
127+ } else if mountType == secretMount && !isAbsTarget(file.Target) {
128+ file.Target = "/run/secrets/" + file.Target
129+ }
130+ }
138131
139- err = s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
140- CopyUIDGID: config.UID != "" || config.GID != "",
141- })
132+ func (s *composeService) setFileOwnership(ctx context.Context, id string, file *types.FileReferenceConfig, ctrConfig *container.Config) (*container.Config, error) {
133+ if file.UID != "" || file.GID != "" {
134+ return ctrConfig, nil
135+ }
136+
137+ if ctrConfig == nil {
138+ ctr, err := s.apiClient().ContainerInspect(ctx, id)
142139 if err != nil {
143- return err
140+ return nil, err
144141 }
142+ ctrConfig = ctr.Config
145143 }
146- return nil
144+
145+ parts := strings.Split(ctrConfig.User, ":")
146+ if len(parts) > 0 {
147+ file.UID = parts[0]
148+ }
149+ if len(parts) > 1 {
150+ file.GID = parts[1]
151+ }
152+
153+ return ctrConfig, nil
154+ }
155+
156+ func (s *composeService) copyFileToContainer(ctx context.Context, id, content string, file types.FileReferenceConfig) error {
157+ b, err := createTar(content, file)
158+ if err != nil {
159+ return err
160+ }
161+
162+ return s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
163+ CopyUIDGID: true,
164+ })
147165}
148166
149167func createTar(env string, config types.FileReferenceConfig) (bytes.Buffer, error) {
0 commit comments