Skip to content

Commit 0c37c10

Browse files
ndeloofglours
authored andcommitted
mount API is not strictly equivalent to bind
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
1 parent 43cc2be commit 0c37c10

4 files changed

Lines changed: 72 additions & 20 deletions

File tree

pkg/compose/build.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
320320
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]api.ImageSummary, error) {
321321
imageNames := utils.Set[string]{}
322322
for _, s := range project.Services {
323-
imgName := api.GetImageNameOrDefault(s, project.Name)
324-
imageNames.Add(imgName)
323+
imageNames.Add(api.GetImageNameOrDefault(s, project.Name))
325324
for _, volume := range s.Volumes {
326325
if volume.Type == types.VolumeTypeImage {
327326
imageNames.Add(volume.Source)

pkg/compose/create.go

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,7 @@ func getDependentServiceFromMode(mode string) string {
828828
return ""
829829
}
830830

831+
//nolint:gocyclo
831832
func (s *composeService) buildContainerVolumes(
832833
ctx context.Context,
833834
p types.Project,
@@ -848,30 +849,37 @@ func (s *composeService) buildContainerVolumes(
848849
return nil, nil, err
849850
}
850851

851-
MOUNTS:
852852
for _, m := range mountOptions {
853-
if m.Type == mount.TypeNamedPipe {
854-
mounts = append(mounts, m)
855-
continue
856-
}
857-
if m.Type == mount.TypeBind {
853+
switch m.Type {
854+
case mount.TypeBind:
858855
// `Mount` is preferred but does not offer option to created host path if missing
859856
// so `Bind` API is used here with raw volume string
860857
// see https://github.com/moby/moby/issues/43483
861-
for _, v := range service.Volumes {
862-
if v.Target == m.Target {
863-
switch {
864-
case string(m.Type) != v.Type:
865-
v.Source = m.Source
866-
fallthrough
867-
case !requireMountAPI(v.Bind):
868-
binds = append(binds, v.String())
869-
continue MOUNTS
858+
v := findVolumeByTarget(service.Volumes, m.Target)
859+
if v != nil {
860+
switch {
861+
case v.Type != types.VolumeTypeBind:
862+
v.Source = m.Source
863+
fallthrough
864+
case !requireMountAPI(v.Bind):
865+
vol := findVolumeByName(p.Volumes, m.Source)
866+
if vol != nil {
867+
binds = append(binds, toBindString(vol.Name, v))
868+
continue
870869
}
871870
}
872871
}
873-
}
874-
if m.Type == mount.TypeImage {
872+
case mount.TypeVolume:
873+
v := findVolumeByTarget(service.Volumes, m.Target)
874+
vol := findVolumeByName(p.Volumes, m.Source)
875+
if v != nil && vol != nil {
876+
if _, ok := vol.DriverOpts["device"]; ok && vol.Driver == "local" && vol.DriverOpts["o"] == "bind" {
877+
// Looks like a volume, but actually a bind mount which requires the bind API
878+
binds = append(binds, toBindString(vol.Name, v))
879+
continue
880+
}
881+
}
882+
case mount.TypeImage:
875883
version, err := s.RuntimeVersion(ctx)
876884
if err != nil {
877885
return nil, nil, err
@@ -885,6 +893,42 @@ MOUNTS:
885893
return binds, mounts, nil
886894
}
887895

896+
func toBindString(name string, v *types.ServiceVolumeConfig) string {
897+
access := "rw"
898+
if v.ReadOnly {
899+
access = "ro"
900+
}
901+
options := []string{access}
902+
if v.Bind != nil && v.Bind.SELinux != "" {
903+
options = append(options, v.Bind.SELinux)
904+
}
905+
if v.Bind != nil && v.Bind.Propagation != "" {
906+
options = append(options, v.Bind.Propagation)
907+
}
908+
if v.Volume != nil && v.Volume.NoCopy {
909+
options = append(options, "nocopy")
910+
}
911+
return fmt.Sprintf("%s:%s:%s", name, v.Target, strings.Join(options, ","))
912+
}
913+
914+
func findVolumeByName(volumes types.Volumes, name string) *types.VolumeConfig {
915+
for _, vol := range volumes {
916+
if vol.Name == name {
917+
return &vol
918+
}
919+
}
920+
return nil
921+
}
922+
923+
func findVolumeByTarget(volumes []types.ServiceVolumeConfig, target string) *types.ServiceVolumeConfig {
924+
for _, v := range volumes {
925+
if v.Target == target {
926+
return &v
927+
}
928+
}
929+
return nil
930+
}
931+
888932
// requireMountAPI check if Bind declaration can be implemented by the plain old Bind API or uses any of the advanced
889933
// options which require use of Mount API
890934
func requireMountAPI(bind *types.ServiceVolumeBind) bool {

pkg/compose/down.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,15 @@ func (s *composeService) removeImage(ctx context.Context, image string, w progre
282282

283283
func (s *composeService) removeVolume(ctx context.Context, id string, w progress.Writer) error {
284284
resource := fmt.Sprintf("Volume %s", id)
285+
286+
_, err := s.apiClient().VolumeInspect(ctx, id)
287+
if errdefs.IsNotFound(err) {
288+
// Already gone
289+
return nil
290+
}
291+
285292
w.Event(progress.NewEvent(resource, progress.Working, "Removing"))
286-
err := s.apiClient().VolumeRemove(ctx, id, true)
293+
err = s.apiClient().VolumeRemove(ctx, id, true)
287294
if err == nil {
288295
w.Event(progress.NewEvent(resource, progress.Done, "Removed"))
289296
return nil

pkg/compose/down_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ func TestDownRemoveVolumes(t *testing.T) {
254254
Return(volume.ListResponse{
255255
Volumes: []*volume.Volume{{Name: "myProject_volume"}},
256256
}, nil)
257+
api.EXPECT().VolumeInspect(gomock.Any(), "myProject_volume").
258+
Return(volume.Volume{}, nil)
257259
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
258260
Return(nil, nil)
259261

0 commit comments

Comments
 (0)