Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit c881e22

Browse files
authored
Merge pull request #1143 from aiordache/local_volume_override
2 parents aaba5b3 + 3e3f94c commit c881e22

File tree

4 files changed

+205
-81
lines changed

4 files changed

+205
-81
lines changed

local/compose/convergence.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ func (s *composeService) ensureService(ctx context.Context, observedState Contai
9393
}
9494

9595
for _, container := range actual {
96-
container := container
9796
name := getCanonicalContainerName(container)
9897

9998
diverged := container.Labels[configHashLabel] != expected
@@ -251,7 +250,7 @@ func (s *composeService) restartContainer(ctx context.Context, container moby.Co
251250
}
252251

253252
func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, container *moby.Container, autoRemove bool) error {
254-
containerConfig, hostConfig, networkingConfig, err := getCreateOptions(project, service, number, container, autoRemove)
253+
containerConfig, hostConfig, networkingConfig, err := s.getCreateOptions(ctx, project, service, number, container, autoRemove)
255254
if err != nil {
256255
return err
257256
}

local/compose/create.go

Lines changed: 193 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
5050

5151
prepareNetworks(project)
5252

53+
err = prepareVolumes(project)
54+
if err != nil {
55+
return err
56+
}
57+
5358
if err := s.ensureNetworks(ctx, project.Networks); err != nil {
5459
return err
5560
}
@@ -91,6 +96,29 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
9196
})
9297
}
9398

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+
94122
func prepareNetworks(project *types.Project) {
95123
for k, network := range project.Networks {
96124
network.Labels = network.Labels.Add(networkLabel, k)
@@ -131,21 +159,23 @@ func getImageName(service types.ServiceConfig, projectName string) string {
131159
return imageName
132160
}
133161

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)
136166
if err != nil {
137167
return nil, nil, nil, err
138168
}
139169

140170
labels := map[string]string{}
141-
for k, v := range s.Labels {
171+
for k, v := range service.Labels {
142172
labels[k] = v
143173
}
144174

145175
labels[projectLabel] = p.Name
146-
labels[serviceLabel] = s.Name
176+
labels[serviceLabel] = service.Name
147177
labels[versionLabel] = ComposeVersion
148-
if _, ok := s.Labels[oneoffLabel]; !ok {
178+
if _, ok := service.Labels[oneoffLabel]; !ok {
149179
labels[oneoffLabel] = "False"
150180
}
151181
labels[configHashLabel] = hash
@@ -157,67 +187,72 @@ func getCreateOptions(p *types.Project, s types.ServiceConfig, number int, inher
157187
runCmd strslice.StrSlice
158188
entrypoint strslice.StrSlice
159189
)
160-
if len(s.Command) > 0 {
161-
runCmd = strslice.StrSlice(s.Command)
190+
if len(service.Command) > 0 {
191+
runCmd = strslice.StrSlice(service.Command)
162192
}
163-
if len(s.Entrypoint) > 0 {
164-
entrypoint = strslice.StrSlice(s.Entrypoint)
193+
if len(service.Entrypoint) > 0 {
194+
entrypoint = strslice.StrSlice(service.Entrypoint)
165195
}
166196

167197
var (
168-
tty = s.Tty
169-
stdinOpen = s.StdinOpen
198+
tty = service.Tty
199+
stdinOpen = service.StdinOpen
170200
attachStdin = false
171201
)
172202

203+
volumeMounts, binds, mounts, err := s.buildContainerVolumes(ctx, *p, service, inherit)
204+
if err != nil {
205+
return nil, nil, nil, err
206+
}
207+
173208
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),
178213
Tty: tty,
179214
OpenStdin: stdinOpen,
180215
StdinOnce: true,
181216
AttachStdin: attachStdin,
182217
AttachStderr: true,
183218
AttachStdout: true,
184219
Cmd: runCmd,
185-
Image: getImageName(s, p.Name),
186-
WorkingDir: s.WorkingDir,
220+
Image: getImageName(service, p.Name),
221+
WorkingDir: service.WorkingDir,
187222
Entrypoint: entrypoint,
188-
NetworkDisabled: s.NetworkMode == "disabled",
189-
MacAddress: s.MacAddress,
223+
NetworkDisabled: service.NetworkMode == "disabled",
224+
MacAddress: service.MacAddress,
190225
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,
197230

198-
mountOptions, err := buildContainerMountOptions(*p, s, inherit)
199-
if err != nil {
200-
return nil, nil, nil, err
231+
StopTimeout: convert.ToSeconds(service.StopGracePeriod),
201232
}
202-
bindings := buildContainerBindingOptions(s)
203233

204-
resources := getDeployResources(s)
205-
networkMode := getNetworkMode(p, s)
234+
portBindings := buildContainerPortBindingOptions(service)
235+
236+
resources := getDeployResources(service)
237+
networkMode := getNetworkMode(p, service)
206238
hostConfig := container.HostConfig{
207239
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),
211244
NetworkMode: networkMode,
212-
Init: s.Init,
213-
ReadonlyRootfs: s.ReadOnly,
245+
Init: service.Init,
246+
ReadonlyRootfs: service.ReadOnly,
214247
// ShmSize: , TODO
215-
Sysctls: s.Sysctls,
216-
PortBindings: bindings,
248+
Sysctls: service.Sysctls,
249+
PortBindings: portBindings,
217250
Resources: resources,
251+
VolumeDriver: service.VolumeDriver,
252+
VolumesFrom: service.VolumesFrom,
218253
}
219254

220-
networkConfig := buildDefaultNetworkConfig(s, networkMode, getContainerName(p.Name, s, number))
255+
networkConfig := buildDefaultNetworkConfig(service, networkMode, getContainerName(p.Name, service, number))
221256
return &containerConfig, &hostConfig, networkConfig, nil
222257
}
223258

@@ -253,7 +288,7 @@ func buildContainerPorts(s types.ServiceConfig) nat.PortSet {
253288
return ports
254289
}
255290

256-
func buildContainerBindingOptions(s types.ServiceConfig) nat.PortMap {
291+
func buildContainerPortBindingOptions(s types.ServiceConfig) nat.PortMap {
257292
bindings := nat.PortMap{}
258293
for _, port := range s.Ports {
259294
p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
@@ -268,9 +303,71 @@ func buildContainerBindingOptions(s types.ServiceConfig) nat.PortMap {
268303
return bindings
269304
}
270305

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{}
274371
if inherit != nil {
275372
for _, m := range inherit.Mounts {
276373
if m.Type == "tmpfs" {
@@ -280,27 +377,56 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, inherit
280377
if m.Type == "volume" {
281378
src = m.Name
282379
}
283-
mounts = append(mounts, mount.Mount{
380+
mounts[m.Destination] = mount.Mount{
284381
Type: m.Type,
285382
Source: src,
286383
Target: m.Destination,
287384
ReadOnly: !m.RW,
288-
})
289-
inherited = append(inherited, m.Destination)
385+
}
290386
}
291387
}
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
292398

293-
for _, v := range s.Volumes {
294-
if contains(inherited, v.Target) {
295-
continue
296399
}
400+
}
401+
for _, v := range s.Volumes {
297402
mount, err := buildMount(p, v)
298403
if err != nil {
299404
return nil, err
300405
}
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
302418
}
303419

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+
304430
secretsDir := "/run/secrets"
305431
for _, secret := range s.Secrets {
306432
target := secret.Target
@@ -315,27 +441,22 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, inherit
315441
return nil, fmt.Errorf("unsupported external secret %s", definedSecret.Name)
316442
}
317443

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-
327444
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,
331449
})
332450
if err != nil {
333451
return nil, err
334452
}
335-
mounts = append(mounts, mount)
453+
mounts[target] = mount
336454
}
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
339460
}
340461

341462
func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.Mount, error) {
@@ -349,10 +470,14 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
349470
}
350471
}
351472
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+
}
355479
}
480+
356481
}
357482

358483
return mount.Mount{

0 commit comments

Comments
 (0)