clean.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package environment // import "github.com/docker/docker/testutil/environment"
  2. import (
  3. "context"
  4. "regexp"
  5. "strings"
  6. "testing"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/filters"
  9. "github.com/docker/docker/client"
  10. "gotest.tools/assert"
  11. )
  12. // Clean the environment, preserving protected objects (images, containers, ...)
  13. // and removing everything else. It's meant to run after any tests so that they don't
  14. // depend on each others.
  15. func (e *Execution) Clean(t testing.TB) {
  16. t.Helper()
  17. client := e.APIClient()
  18. platform := e.OSType
  19. if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
  20. unpauseAllContainers(t, client)
  21. }
  22. deleteAllContainers(t, client, e.protectedElements.containers)
  23. deleteAllImages(t, client, e.protectedElements.images)
  24. deleteAllVolumes(t, client, e.protectedElements.volumes)
  25. deleteAllNetworks(t, client, platform, e.protectedElements.networks)
  26. if platform == "linux" {
  27. deleteAllPlugins(t, client, e.protectedElements.plugins)
  28. }
  29. }
  30. func unpauseAllContainers(t testing.TB, client client.ContainerAPIClient) {
  31. t.Helper()
  32. ctx := context.Background()
  33. containers := getPausedContainers(ctx, t, client)
  34. if len(containers) > 0 {
  35. for _, container := range containers {
  36. err := client.ContainerUnpause(ctx, container.ID)
  37. assert.Check(t, err, "failed to unpause container %s", container.ID)
  38. }
  39. }
  40. }
  41. func getPausedContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container {
  42. t.Helper()
  43. filter := filters.NewArgs()
  44. filter.Add("status", "paused")
  45. containers, err := client.ContainerList(ctx, types.ContainerListOptions{
  46. Filters: filter,
  47. Quiet: true,
  48. All: true,
  49. })
  50. assert.Check(t, err, "failed to list containers")
  51. return containers
  52. }
  53. var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
  54. func deleteAllContainers(t testing.TB, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) {
  55. t.Helper()
  56. ctx := context.Background()
  57. containers := getAllContainers(ctx, t, apiclient)
  58. if len(containers) == 0 {
  59. return
  60. }
  61. for _, container := range containers {
  62. if _, ok := protectedContainers[container.ID]; ok {
  63. continue
  64. }
  65. err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
  66. Force: true,
  67. RemoveVolumes: true,
  68. })
  69. if err == nil || client.IsErrNotFound(err) || alreadyExists.MatchString(err.Error()) || isErrNotFoundSwarmClassic(err) {
  70. continue
  71. }
  72. assert.Check(t, err, "failed to remove %s", container.ID)
  73. }
  74. }
  75. func getAllContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container {
  76. t.Helper()
  77. containers, err := client.ContainerList(ctx, types.ContainerListOptions{
  78. Quiet: true,
  79. All: true,
  80. })
  81. assert.Check(t, err, "failed to list containers")
  82. return containers
  83. }
  84. func deleteAllImages(t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
  85. t.Helper()
  86. images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
  87. assert.Check(t, err, "failed to list images")
  88. ctx := context.Background()
  89. for _, image := range images {
  90. tags := tagsFromImageSummary(image)
  91. if len(tags) == 0 {
  92. removeImage(ctx, t, apiclient, image.ID)
  93. continue
  94. }
  95. for _, tag := range tags {
  96. if _, ok := protectedImages[tag]; !ok {
  97. removeImage(ctx, t, apiclient, tag)
  98. }
  99. }
  100. }
  101. }
  102. func removeImage(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, ref string) {
  103. t.Helper()
  104. _, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{
  105. Force: true,
  106. })
  107. if client.IsErrNotFound(err) {
  108. return
  109. }
  110. assert.Check(t, err, "failed to remove image %s", ref)
  111. }
  112. func deleteAllVolumes(t testing.TB, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) {
  113. t.Helper()
  114. volumes, err := c.VolumeList(context.Background(), filters.Args{})
  115. assert.Check(t, err, "failed to list volumes")
  116. for _, v := range volumes.Volumes {
  117. if _, ok := protectedVolumes[v.Name]; ok {
  118. continue
  119. }
  120. err := c.VolumeRemove(context.Background(), v.Name, true)
  121. // Docker EE may list volumes that no longer exist.
  122. if isErrNotFoundSwarmClassic(err) {
  123. continue
  124. }
  125. assert.Check(t, err, "failed to remove volume %s", v.Name)
  126. }
  127. }
  128. func deleteAllNetworks(t testing.TB, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) {
  129. t.Helper()
  130. networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
  131. assert.Check(t, err, "failed to list networks")
  132. for _, n := range networks {
  133. if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
  134. continue
  135. }
  136. if _, ok := protectedNetworks[n.ID]; ok {
  137. continue
  138. }
  139. if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
  140. // nat is a pre-defined network on Windows and cannot be removed
  141. continue
  142. }
  143. err := c.NetworkRemove(context.Background(), n.ID)
  144. assert.Check(t, err, "failed to remove network %s", n.ID)
  145. }
  146. }
  147. func deleteAllPlugins(t testing.TB, c client.PluginAPIClient, protectedPlugins map[string]struct{}) {
  148. t.Helper()
  149. plugins, err := c.PluginList(context.Background(), filters.Args{})
  150. // Docker EE does not allow cluster-wide plugin management.
  151. if client.IsErrNotImplemented(err) {
  152. return
  153. }
  154. assert.Check(t, err, "failed to list plugins")
  155. for _, p := range plugins {
  156. if _, ok := protectedPlugins[p.Name]; ok {
  157. continue
  158. }
  159. err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
  160. assert.Check(t, err, "failed to remove plugin %s", p.ID)
  161. }
  162. }
  163. // Swarm classic aggregates node errors and returns a 500 so we need to check
  164. // the error string instead of just IsErrNotFound().
  165. func isErrNotFoundSwarmClassic(err error) bool {
  166. return err != nil && strings.Contains(strings.ToLower(err.Error()), "no such")
  167. }