diff --git a/integration/container/stop_test.go b/integration/container/stop_test.go new file mode 100644 index 0000000000..feecc6901f --- /dev/null +++ b/integration/container/stop_test.go @@ -0,0 +1,74 @@ +package container + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" + "github.com/docker/docker/integration/util/request" + "github.com/gotestyourself/gotestyourself/icmd" + "github.com/gotestyourself/gotestyourself/poll" + "github.com/gotestyourself/gotestyourself/skip" + "github.com/stretchr/testify/require" +) + +func TestDeleteDevicemapper(t *testing.T) { + skip.IfCondition(t, testEnv.DaemonInfo.Driver != "devicemapper") + + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + foo, err := client.ContainerCreate(ctx, + &container.Config{ + Cmd: []string{"echo"}, + Image: "busybox", + }, + &container.HostConfig{}, + &network.NetworkingConfig{}, + "foo", + ) + require.NoError(t, err) + + err = client.ContainerStart(ctx, foo.ID, types.ContainerStartOptions{}) + require.NoError(t, err) + + inspect, err := client.ContainerInspect(ctx, foo.ID) + require.NoError(t, err) + + poll.WaitOn(t, containerIsStopped(ctx, client, foo.ID), poll.WithDelay(100*time.Millisecond)) + + deviceID := inspect.GraphDriver.Data["DeviceId"] + + // Find pool name from device name + deviceName := inspect.GraphDriver.Data["DeviceName"] + devicePrefix := deviceName[:strings.LastIndex(deviceName, "-")] + devicePool := fmt.Sprintf("/dev/mapper/%s-pool", devicePrefix) + + result := icmd.RunCommand("dmsetup", "message", devicePool, "0", fmt.Sprintf("delete %s", deviceID)) + result.Assert(t, icmd.Success) + + err = client.ContainerRemove(ctx, foo.ID, types.ContainerRemoveOptions{}) + require.NoError(t, err) +} + +func containerIsStopped(ctx context.Context, client client.APIClient, containerID string) func(log poll.LogT) poll.Result { + return func(log poll.LogT) poll.Result { + inspect, err := client.ContainerInspect(ctx, containerID) + + switch { + case err != nil: + return poll.Error(err) + case !inspect.State.Running: + return poll.Success() + default: + return poll.Continue("waiting for container to be stopped") + } + } +} diff --git a/pkg/devicemapper/devmapper.go b/pkg/devicemapper/devmapper.go index 48618765fb..c72992f7a1 100644 --- a/pkg/devicemapper/devmapper.go +++ b/pkg/devicemapper/devmapper.go @@ -67,12 +67,14 @@ var ( ErrBusy = errors.New("Device is Busy") ErrDeviceIDExists = errors.New("Device Id Exists") ErrEnxio = errors.New("No such device or address") + ErrEnoData = errors.New("No data available") ) var ( - dmSawBusy bool - dmSawExist bool - dmSawEnxio bool // No Such Device or Address + dmSawBusy bool + dmSawExist bool + dmSawEnxio bool // No Such Device or Address + dmSawEnoData bool // No data available ) type ( @@ -708,10 +710,15 @@ func DeleteDevice(poolName string, deviceID int) error { } dmSawBusy = false + dmSawEnoData = false if err := task.run(); err != nil { if dmSawBusy { return ErrBusy } + if dmSawEnoData { + logrus.Debugf("devicemapper: Device(id: %d) from pool(%s) does not exist", deviceID, poolName) + return nil + } return fmt.Errorf("devicemapper: Error running DeleteDevice %s", err) } return nil diff --git a/pkg/devicemapper/devmapper_log.go b/pkg/devicemapper/devmapper_log.go index f2ac7da87c..1da75101cf 100644 --- a/pkg/devicemapper/devmapper_log.go +++ b/pkg/devicemapper/devmapper_log.go @@ -55,6 +55,9 @@ func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, if strings.Contains(msg, "No such device or address") { dmSawEnxio = true } + if strings.Contains(msg, "No data available") { + dmSawEnoData = true + } } if dmLogger != nil {