mirror of
https://github.com/docker/compose.git
synced 2025-07-07 05:44:25 +02:00
Merge pull request #9281 from ndeloof/down_volume_external
don't remove external volumes/networks
This commit is contained in:
commit
d999c230a5
@ -24,15 +24,15 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/cli/streams"
|
"github.com/docker/cli/cli/streams"
|
||||||
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/sanathkr/go-yaml"
|
"github.com/sanathkr/go-yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -194,3 +194,39 @@ func (s *composeService) actualState(ctx context.Context, projectName string, se
|
|||||||
}
|
}
|
||||||
return containers, project, nil
|
return containers, project, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *composeService) actualVolumes(ctx context.Context, projectName string) (types.Volumes, error) {
|
||||||
|
volumes, err := s.apiClient().VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := types.Volumes{}
|
||||||
|
for _, vol := range volumes.Volumes {
|
||||||
|
actual[vol.Labels[api.VolumeLabel]] = types.VolumeConfig{
|
||||||
|
Name: vol.Name,
|
||||||
|
Driver: vol.Driver,
|
||||||
|
Labels: vol.Labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actual, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) actualNetworks(ctx context.Context, projectName string) (types.Networks, error) {
|
||||||
|
networks, err := s.apiClient().NetworkList(ctx, moby.NetworkListOptions{
|
||||||
|
Filters: filters.NewArgs(projectFilter(projectName)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := types.Networks{}
|
||||||
|
for _, net := range networks {
|
||||||
|
actual[net.Labels[api.NetworkLabel]] = types.NetworkConfig{
|
||||||
|
Name: net.Name,
|
||||||
|
Driver: net.Driver,
|
||||||
|
Labels: net.Labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actual, nil
|
||||||
|
}
|
||||||
|
@ -1078,14 +1078,13 @@ func (s *composeService) ensureNetwork(ctx context.Context, n types.NetworkConfi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) removeNetwork(ctx context.Context, networkID string, networkName string) error {
|
func (s *composeService) removeNetwork(ctx context.Context, network string, w progress.Writer) error {
|
||||||
w := progress.ContextWriter(ctx)
|
eventName := fmt.Sprintf("Network %s", network)
|
||||||
eventName := fmt.Sprintf("Network %s", networkName)
|
|
||||||
w.Event(progress.RemovingEvent(eventName))
|
w.Event(progress.RemovingEvent(eventName))
|
||||||
|
|
||||||
if err := s.apiClient().NetworkRemove(ctx, networkID); err != nil {
|
if err := s.apiClient().NetworkRemove(ctx, network); err != nil {
|
||||||
w.Event(progress.ErrorEvent(eventName))
|
w.Event(progress.ErrorEvent(eventName))
|
||||||
return errors.Wrapf(err, fmt.Sprintf("failed to remove network %s", networkID))
|
return errors.Wrapf(err, fmt.Sprintf("failed to remove network %s", network))
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Event(progress.RemovedEvent(eventName))
|
w.Event(progress.RemovedEvent(eventName))
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
@ -41,7 +40,6 @@ func (s *composeService) Down(ctx context.Context, projectName string, options a
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) down(ctx context.Context, projectName string, options api.DownOptions) error {
|
func (s *composeService) down(ctx context.Context, projectName string, options api.DownOptions) error {
|
||||||
builtFromResources := options.Project == nil
|
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
resourceToRemove := false
|
resourceToRemove := false
|
||||||
|
|
||||||
@ -51,8 +49,9 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if builtFromResources {
|
project := options.Project
|
||||||
options.Project, err = s.getProjectWithVolumes(ctx, containers, projectName)
|
if project == nil {
|
||||||
|
project, err = s.getProjectWithResources(ctx, containers, projectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -62,7 +61,7 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||||||
resourceToRemove = true
|
resourceToRemove = true
|
||||||
}
|
}
|
||||||
|
|
||||||
err = InReverseDependencyOrder(ctx, options.Project, func(c context.Context, service string) error {
|
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
|
||||||
serviceContainers := containers.filter(isService(service))
|
serviceContainers := containers.filter(isService(service))
|
||||||
err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes)
|
err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes)
|
||||||
return err
|
return err
|
||||||
@ -71,7 +70,7 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
orphans := containers.filter(isNotService(options.Project.ServiceNames()...))
|
orphans := containers.filter(isNotService(project.ServiceNames()...))
|
||||||
if options.RemoveOrphans && len(orphans) > 0 {
|
if options.RemoveOrphans && len(orphans) > 0 {
|
||||||
err := s.removeContainers(ctx, w, orphans, options.Timeout, false)
|
err := s.removeContainers(ctx, w, orphans, options.Timeout, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -79,17 +78,14 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ops, err := s.ensureNetworksDown(ctx, projectName)
|
ops := s.ensureNetworksDown(ctx, project, w)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Images != "" {
|
if options.Images != "" {
|
||||||
ops = append(ops, s.ensureImagesDown(ctx, projectName, options, w)...)
|
ops = append(ops, s.ensureImagesDown(ctx, projectName, options, w)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Volumes {
|
if options.Volumes {
|
||||||
ops = append(ops, s.ensureVolumesDown(ctx, options.Project, w)...)
|
ops = append(ops, s.ensureVolumesDown(ctx, project, w)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !resourceToRemove && len(ops) == 0 {
|
if !resourceToRemove && len(ops) == 0 {
|
||||||
@ -106,6 +102,9 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||||||
func (s *composeService) ensureVolumesDown(ctx context.Context, project *types.Project, w progress.Writer) []downOp {
|
func (s *composeService) ensureVolumesDown(ctx context.Context, project *types.Project, w progress.Writer) []downOp {
|
||||||
var ops []downOp
|
var ops []downOp
|
||||||
for _, vol := range project.Volumes {
|
for _, vol := range project.Volumes {
|
||||||
|
if vol.External.External {
|
||||||
|
continue
|
||||||
|
}
|
||||||
volumeName := vol.Name
|
volumeName := vol.Name
|
||||||
ops = append(ops, func() error {
|
ops = append(ops, func() error {
|
||||||
return s.removeVolume(ctx, volumeName, w)
|
return s.removeVolume(ctx, volumeName, w)
|
||||||
@ -125,20 +124,18 @@ func (s *composeService) ensureImagesDown(ctx context.Context, projectName strin
|
|||||||
return ops
|
return ops
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) ensureNetworksDown(ctx context.Context, projectName string) ([]downOp, error) {
|
func (s *composeService) ensureNetworksDown(ctx context.Context, project *types.Project, w progress.Writer) []downOp {
|
||||||
var ops []downOp
|
var ops []downOp
|
||||||
networks, err := s.apiClient().NetworkList(ctx, moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(projectName))})
|
for _, n := range project.Networks {
|
||||||
if err != nil {
|
if n.External.External {
|
||||||
return ops, err
|
continue
|
||||||
}
|
}
|
||||||
for _, n := range networks {
|
|
||||||
networkID := n.ID
|
|
||||||
networkName := n.Name
|
networkName := n.Name
|
||||||
ops = append(ops, func() error {
|
ops = append(ops, func() error {
|
||||||
return s.removeNetwork(ctx, networkID, networkName)
|
return s.removeNetwork(ctx, networkName, w)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return ops, nil
|
return ops
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) getServiceImages(options api.DownOptions, projectName string) map[string]struct{} {
|
func (s *composeService) getServiceImages(options api.DownOptions, projectName string) map[string]struct{} {
|
||||||
@ -233,21 +230,20 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
|
|||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) getProjectWithVolumes(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
|
func (s *composeService) getProjectWithResources(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
|
||||||
containers = containers.filter(isNotOneOff)
|
containers = containers.filter(isNotOneOff)
|
||||||
project, _ := s.projectFromName(containers, projectName)
|
project, _ := s.projectFromName(containers, projectName)
|
||||||
volumes, err := s.apiClient().VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
|
|
||||||
|
volumes, err := s.actualVolumes(ctx, projectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
project.Volumes = volumes
|
||||||
|
|
||||||
project.Volumes = types.Volumes{}
|
networks, err := s.actualNetworks(ctx, projectName)
|
||||||
for _, vol := range volumes.Volumes {
|
if err != nil {
|
||||||
project.Volumes[vol.Labels[api.VolumeLabel]] = types.VolumeConfig{
|
return nil, err
|
||||||
Name: vol.Name,
|
|
||||||
Driver: vol.Driver,
|
|
||||||
Labels: vol.Labels,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
project.Networks = networks
|
||||||
return project, nil
|
return project, nil
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ func TestDown(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
||||||
Return(volume.VolumeListOKBody{}, nil)
|
Return(volume.VolumeListOKBody{}, nil)
|
||||||
|
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||||
|
Return([]moby.NetworkResource{{Name: "myProject_default"}}, nil)
|
||||||
|
|
||||||
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
||||||
api.EXPECT().ContainerStop(gomock.Any(), "456", nil).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "456", nil).Return(nil)
|
||||||
@ -58,9 +60,6 @@ func TestDown(t *testing.T) {
|
|||||||
api.EXPECT().ContainerRemove(gomock.Any(), "456", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
api.EXPECT().ContainerRemove(gomock.Any(), "456", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
||||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
||||||
|
|
||||||
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).Return([]moby.NetworkResource{{ID: "myProject_default"}},
|
|
||||||
nil)
|
|
||||||
|
|
||||||
api.EXPECT().NetworkRemove(gomock.Any(), "myProject_default").Return(nil)
|
api.EXPECT().NetworkRemove(gomock.Any(), "myProject_default").Return(nil)
|
||||||
|
|
||||||
err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{})
|
err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{})
|
||||||
@ -84,6 +83,8 @@ func TestDownRemoveOrphans(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
||||||
Return(volume.VolumeListOKBody{}, nil)
|
Return(volume.VolumeListOKBody{}, nil)
|
||||||
|
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||||
|
Return([]moby.NetworkResource{{Name: "myProject_default"}}, nil)
|
||||||
|
|
||||||
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
||||||
api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil)
|
||||||
@ -93,9 +94,6 @@ func TestDownRemoveOrphans(t *testing.T) {
|
|||||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
||||||
api.EXPECT().ContainerRemove(gomock.Any(), "321", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
api.EXPECT().ContainerRemove(gomock.Any(), "321", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
||||||
|
|
||||||
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).Return([]moby.NetworkResource{{ID: "myProject_default"}},
|
|
||||||
nil)
|
|
||||||
|
|
||||||
api.EXPECT().NetworkRemove(gomock.Any(), "myProject_default").Return(nil)
|
api.EXPECT().NetworkRemove(gomock.Any(), "myProject_default").Return(nil)
|
||||||
|
|
||||||
err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{RemoveOrphans: true})
|
err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{RemoveOrphans: true})
|
||||||
@ -117,12 +115,12 @@ func TestDownRemoveVolumes(t *testing.T) {
|
|||||||
Return(volume.VolumeListOKBody{
|
Return(volume.VolumeListOKBody{
|
||||||
Volumes: []*moby.Volume{{Name: "myProject_volume"}},
|
Volumes: []*moby.Volume{{Name: "myProject_volume"}},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||||
|
Return(nil, nil)
|
||||||
|
|
||||||
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
||||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
|
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
|
||||||
|
|
||||||
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).Return(nil, nil)
|
|
||||||
|
|
||||||
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
|
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
|
||||||
|
|
||||||
err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Volumes: true})
|
err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Volumes: true})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user