@@ -50,6 +50,11 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
50
50
51
51
prepareNetworks (project )
52
52
53
+ err = prepareVolumes (project )
54
+ if err != nil {
55
+ return err
56
+ }
57
+
53
58
if err := s .ensureNetworks (ctx , project .Networks ); err != nil {
54
59
return err
55
60
}
@@ -91,6 +96,29 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
91
96
})
92
97
}
93
98
99
+ func prepareVolumes (p * types.Project ) error {
100
+ for i := range p .Services {
101
+ volumesFrom , dependServices , err := getVolumesFrom (p , p .Services [i ].VolumesFrom )
102
+ if err != nil {
103
+ return err
104
+ }
105
+ p .Services [i ].VolumesFrom = volumesFrom
106
+ if len (dependServices ) > 0 {
107
+ if p .Services [i ].DependsOn == nil {
108
+ p .Services [i ].DependsOn = make (types.DependsOnConfig , len (dependServices ))
109
+ }
110
+ for _ , service := range p .Services {
111
+ if contains (dependServices , service .Name ) {
112
+ p .Services [i ].DependsOn [service .Name ] = types.ServiceDependency {
113
+ Condition : types .ServiceConditionStarted ,
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ return nil
120
+ }
121
+
94
122
func prepareNetworks (project * types.Project ) {
95
123
for k , network := range project .Networks {
96
124
network .Labels = network .Labels .Add (networkLabel , k )
@@ -131,21 +159,23 @@ func getImageName(service types.ServiceConfig, projectName string) string {
131
159
return imageName
132
160
}
133
161
134
- func getCreateOptions (p * types.Project , s types.ServiceConfig , number int , inherit * moby.Container , autoRemove bool ) (* container.Config , * container.HostConfig , * network.NetworkingConfig , error ) {
135
- hash , err := jsonHash (s )
162
+ func (s * composeService ) getCreateOptions (ctx context.Context , p * types.Project , service types.ServiceConfig , number int , inherit * moby.Container ,
163
+ autoRemove bool ) (* container.Config , * container.HostConfig , * network.NetworkingConfig , error ) {
164
+
165
+ hash , err := jsonHash (service )
136
166
if err != nil {
137
167
return nil , nil , nil , err
138
168
}
139
169
140
170
labels := map [string ]string {}
141
- for k , v := range s .Labels {
171
+ for k , v := range service .Labels {
142
172
labels [k ] = v
143
173
}
144
174
145
175
labels [projectLabel ] = p .Name
146
- labels [serviceLabel ] = s .Name
176
+ labels [serviceLabel ] = service .Name
147
177
labels [versionLabel ] = ComposeVersion
148
- if _ , ok := s .Labels [oneoffLabel ]; ! ok {
178
+ if _ , ok := service .Labels [oneoffLabel ]; ! ok {
149
179
labels [oneoffLabel ] = "False"
150
180
}
151
181
labels [configHashLabel ] = hash
@@ -157,67 +187,72 @@ func getCreateOptions(p *types.Project, s types.ServiceConfig, number int, inher
157
187
runCmd strslice.StrSlice
158
188
entrypoint strslice.StrSlice
159
189
)
160
- if len (s .Command ) > 0 {
161
- runCmd = strslice .StrSlice (s .Command )
190
+ if len (service .Command ) > 0 {
191
+ runCmd = strslice .StrSlice (service .Command )
162
192
}
163
- if len (s .Entrypoint ) > 0 {
164
- entrypoint = strslice .StrSlice (s .Entrypoint )
193
+ if len (service .Entrypoint ) > 0 {
194
+ entrypoint = strslice .StrSlice (service .Entrypoint )
165
195
}
166
196
167
197
var (
168
- tty = s .Tty
169
- stdinOpen = s .StdinOpen
198
+ tty = service .Tty
199
+ stdinOpen = service .StdinOpen
170
200
attachStdin = false
171
201
)
172
202
203
+ volumeMounts , binds , mounts , err := s .buildContainerVolumes (ctx , * p , service , inherit )
204
+ if err != nil {
205
+ return nil , nil , nil , err
206
+ }
207
+
173
208
containerConfig := container.Config {
174
- Hostname : s .Hostname ,
175
- Domainname : s .DomainName ,
176
- User : s .User ,
177
- ExposedPorts : buildContainerPorts (s ),
209
+ Hostname : service .Hostname ,
210
+ Domainname : service .DomainName ,
211
+ User : service .User ,
212
+ ExposedPorts : buildContainerPorts (service ),
178
213
Tty : tty ,
179
214
OpenStdin : stdinOpen ,
180
215
StdinOnce : true ,
181
216
AttachStdin : attachStdin ,
182
217
AttachStderr : true ,
183
218
AttachStdout : true ,
184
219
Cmd : runCmd ,
185
- Image : getImageName (s , p .Name ),
186
- WorkingDir : s .WorkingDir ,
220
+ Image : getImageName (service , p .Name ),
221
+ WorkingDir : service .WorkingDir ,
187
222
Entrypoint : entrypoint ,
188
- NetworkDisabled : s .NetworkMode == "disabled" ,
189
- MacAddress : s .MacAddress ,
223
+ NetworkDisabled : service .NetworkMode == "disabled" ,
224
+ MacAddress : service .MacAddress ,
190
225
Labels : labels ,
191
- StopSignal : s .StopSignal ,
192
- Env : convert .ToMobyEnv (s .Environment ),
193
- Healthcheck : convert .ToMobyHealthCheck (s .HealthCheck ),
194
- // Volumes: // FIXME unclear to me the overlap with HostConfig.Mounts
195
- StopTimeout : convert .ToSeconds (s .StopGracePeriod ),
196
- }
226
+ StopSignal : service .StopSignal ,
227
+ Env : convert .ToMobyEnv (service .Environment ),
228
+ Healthcheck : convert .ToMobyHealthCheck (service .HealthCheck ),
229
+ Volumes : volumeMounts ,
197
230
198
- mountOptions , err := buildContainerMountOptions (* p , s , inherit )
199
- if err != nil {
200
- return nil , nil , nil , err
231
+ StopTimeout : convert .ToSeconds (service .StopGracePeriod ),
201
232
}
202
- bindings := buildContainerBindingOptions (s )
203
233
204
- resources := getDeployResources (s )
205
- networkMode := getNetworkMode (p , s )
234
+ portBindings := buildContainerPortBindingOptions (service )
235
+
236
+ resources := getDeployResources (service )
237
+ networkMode := getNetworkMode (p , service )
206
238
hostConfig := container.HostConfig {
207
239
AutoRemove : autoRemove ,
208
- Mounts : mountOptions ,
209
- CapAdd : strslice .StrSlice (s .CapAdd ),
210
- CapDrop : strslice .StrSlice (s .CapDrop ),
240
+ Binds : binds ,
241
+ Mounts : mounts ,
242
+ CapAdd : strslice .StrSlice (service .CapAdd ),
243
+ CapDrop : strslice .StrSlice (service .CapDrop ),
211
244
NetworkMode : networkMode ,
212
- Init : s .Init ,
213
- ReadonlyRootfs : s .ReadOnly ,
245
+ Init : service .Init ,
246
+ ReadonlyRootfs : service .ReadOnly ,
214
247
// ShmSize: , TODO
215
- Sysctls : s .Sysctls ,
216
- PortBindings : bindings ,
248
+ Sysctls : service .Sysctls ,
249
+ PortBindings : portBindings ,
217
250
Resources : resources ,
251
+ VolumeDriver : service .VolumeDriver ,
252
+ VolumesFrom : service .VolumesFrom ,
218
253
}
219
254
220
- networkConfig := buildDefaultNetworkConfig (s , networkMode , getContainerName (p .Name , s , number ))
255
+ networkConfig := buildDefaultNetworkConfig (service , networkMode , getContainerName (p .Name , service , number ))
221
256
return & containerConfig , & hostConfig , networkConfig , nil
222
257
}
223
258
@@ -253,7 +288,7 @@ func buildContainerPorts(s types.ServiceConfig) nat.PortSet {
253
288
return ports
254
289
}
255
290
256
- func buildContainerBindingOptions (s types.ServiceConfig ) nat.PortMap {
291
+ func buildContainerPortBindingOptions (s types.ServiceConfig ) nat.PortMap {
257
292
bindings := nat.PortMap {}
258
293
for _ , port := range s .Ports {
259
294
p := nat .Port (fmt .Sprintf ("%d/%s" , port .Target , port .Protocol ))
@@ -268,9 +303,71 @@ func buildContainerBindingOptions(s types.ServiceConfig) nat.PortMap {
268
303
return bindings
269
304
}
270
305
271
- func buildContainerMountOptions (p types.Project , s types.ServiceConfig , inherit * moby.Container ) ([]mount.Mount , error ) {
272
- mounts := []mount.Mount {}
273
- var inherited []string
306
+ func getVolumesFrom (project * types.Project , volumesFrom []string ) ([]string , []string , error ) {
307
+ var volumes = []string {}
308
+ var services = []string {}
309
+ // parse volumes_from
310
+ if len (volumesFrom ) == 0 {
311
+ return volumes , services , nil
312
+ }
313
+ for _ , vol := range volumesFrom {
314
+ spec := strings .Split (vol , ":" )
315
+ if spec [0 ] == "container" {
316
+ volumes = append (volumes , strings .Join (spec [1 :], ":" ))
317
+ continue
318
+ }
319
+ serviceName := spec [0 ]
320
+ services = append (services , serviceName )
321
+ service , err := project .GetService (serviceName )
322
+ if err != nil {
323
+ return nil , nil , err
324
+ }
325
+
326
+ firstContainer := getContainerName (project .Name , service , 1 )
327
+ v := fmt .Sprintf ("%s:%s" , firstContainer , strings .Join (spec [1 :], ":" ))
328
+ volumes = append (volumes , v )
329
+ }
330
+ return volumes , services , nil
331
+
332
+ }
333
+
334
+ func (s * composeService ) buildContainerVolumes (ctx context.Context , p types.Project , service types.ServiceConfig ,
335
+ inherit * moby.Container ) (map [string ]struct {}, []string , []mount.Mount , error ) {
336
+ var mounts = []mount.Mount {}
337
+
338
+ image := getImageName (service , p .Name )
339
+ imgInspect , _ , err := s .apiClient .ImageInspectWithRaw (ctx , image )
340
+ if err != nil {
341
+ return nil , nil , nil , err
342
+ }
343
+
344
+ mountOptions , err := buildContainerMountOptions (p , service , imgInspect , inherit )
345
+ if err != nil {
346
+ return nil , nil , nil , err
347
+ }
348
+
349
+ // filter binds and volumes mount targets
350
+ volumeMounts := map [string ]struct {}{}
351
+ binds := []string {}
352
+ for _ , m := range mountOptions {
353
+
354
+ if m .Type == mount .TypeVolume {
355
+ volumeMounts [m .Target ] = struct {}{}
356
+ if m .Source != "" {
357
+ binds = append (binds , fmt .Sprintf ("%s:%s:rw" , m .Source , m .Target ))
358
+ }
359
+ }
360
+ }
361
+ for _ , m := range mountOptions {
362
+ if m .Type == mount .TypeBind || m .Type == mount .TypeTmpfs {
363
+ mounts = append (mounts , m )
364
+ }
365
+ }
366
+ return volumeMounts , binds , mounts , nil
367
+ }
368
+
369
+ func buildContainerMountOptions (p types.Project , s types.ServiceConfig , img moby.ImageInspect , inherit * moby.Container ) ([]mount.Mount , error ) {
370
+ var mounts = map [string ]mount.Mount {}
274
371
if inherit != nil {
275
372
for _ , m := range inherit .Mounts {
276
373
if m .Type == "tmpfs" {
@@ -280,27 +377,56 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, inherit
280
377
if m .Type == "volume" {
281
378
src = m .Name
282
379
}
283
- mounts = append ( mounts , mount.Mount {
380
+ mounts [ m . Destination ] = mount.Mount {
284
381
Type : m .Type ,
285
382
Source : src ,
286
383
Target : m .Destination ,
287
384
ReadOnly : ! m .RW ,
288
- })
289
- inherited = append (inherited , m .Destination )
385
+ }
290
386
}
291
387
}
388
+ if img .ContainerConfig != nil {
389
+ for k := range img .ContainerConfig .Volumes {
390
+ mount , err := buildMount (p , types.ServiceVolumeConfig {
391
+ Type : types .VolumeTypeVolume ,
392
+ Target : k ,
393
+ })
394
+ if err != nil {
395
+ return nil , err
396
+ }
397
+ mounts [k ] = mount
292
398
293
- for _ , v := range s .Volumes {
294
- if contains (inherited , v .Target ) {
295
- continue
296
399
}
400
+ }
401
+ for _ , v := range s .Volumes {
297
402
mount , err := buildMount (p , v )
298
403
if err != nil {
299
404
return nil , err
300
405
}
301
- mounts = append (mounts , mount )
406
+ mounts [mount .Target ] = mount
407
+ }
408
+
409
+ secrets , err := buildContainerSecretMounts (p , s )
410
+ if err != nil {
411
+ return nil , err
412
+ }
413
+ for _ , s := range secrets {
414
+ if _ , found := mounts [s .Target ]; found {
415
+ continue
416
+ }
417
+ mounts [s .Target ] = s
302
418
}
303
419
420
+ values := make ([]mount.Mount , 0 , len (mounts ))
421
+ for _ , v := range mounts {
422
+ values = append (values , v )
423
+ }
424
+ return values , nil
425
+ }
426
+
427
+ func buildContainerSecretMounts (p types.Project , s types.ServiceConfig ) ([]mount.Mount , error ) {
428
+ var mounts = map [string ]mount.Mount {}
429
+
304
430
secretsDir := "/run/secrets"
305
431
for _ , secret := range s .Secrets {
306
432
target := secret .Target
@@ -315,27 +441,22 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, inherit
315
441
return nil , fmt .Errorf ("unsupported external secret %s" , definedSecret .Name )
316
442
}
317
443
318
- if contains (inherited , target ) {
319
- // remove inherited mount
320
- pos := indexOf (inherited , target )
321
- if pos >= 0 {
322
- mounts = append (mounts [:pos ], mounts [pos + 1 ])
323
- inherited = append (inherited [:pos ], inherited [pos + 1 ])
324
- }
325
- }
326
-
327
444
mount , err := buildMount (p , types.ServiceVolumeConfig {
328
- Type : types .VolumeTypeBind ,
329
- Source : definedSecret .File ,
330
- Target : target ,
445
+ Type : types .VolumeTypeBind ,
446
+ Source : definedSecret .File ,
447
+ Target : target ,
448
+ ReadOnly : true ,
331
449
})
332
450
if err != nil {
333
451
return nil , err
334
452
}
335
- mounts = append ( mounts , mount )
453
+ mounts [ target ] = mount
336
454
}
337
-
338
- return mounts , nil
455
+ values := make ([]mount.Mount , 0 , len (mounts ))
456
+ for _ , v := range mounts {
457
+ values = append (values , v )
458
+ }
459
+ return values , nil
339
460
}
340
461
341
462
func buildMount (project types.Project , volume types.ServiceVolumeConfig ) (mount.Mount , error ) {
@@ -349,10 +470,14 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
349
470
}
350
471
}
351
472
if volume .Type == types .VolumeTypeVolume {
352
- pVolume , ok := project .Volumes [volume .Source ]
353
- if ok {
354
- source = pVolume .Name
473
+ if volume .Source != "" {
474
+
475
+ pVolume , ok := project .Volumes [volume .Source ]
476
+ if ok {
477
+ source = pVolume .Name
478
+ }
355
479
}
480
+
356
481
}
357
482
358
483
return mount.Mount {
0 commit comments