@@ -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