protect.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package environment
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/docker/docker/api/types"
  6. "github.com/docker/docker/api/types/filters"
  7. "github.com/docker/docker/api/types/volume"
  8. "github.com/docker/docker/errdefs"
  9. "gotest.tools/v3/assert"
  10. )
  11. var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bullseye-slim"}
  12. type protectedElements struct {
  13. containers map[string]struct{}
  14. images map[string]struct{}
  15. networks map[string]struct{}
  16. plugins map[string]struct{}
  17. volumes map[string]struct{}
  18. }
  19. func newProtectedElements() protectedElements {
  20. return protectedElements{
  21. containers: map[string]struct{}{},
  22. images: map[string]struct{}{},
  23. networks: map[string]struct{}{},
  24. plugins: map[string]struct{}{},
  25. volumes: map[string]struct{}{},
  26. }
  27. }
  28. // ProtectAll protects the existing environment (containers, images, networks,
  29. // volumes, and, on Linux, plugins) from being cleaned up at the end of test
  30. // runs
  31. func ProtectAll(t testing.TB, testEnv *Execution) {
  32. t.Helper()
  33. ProtectContainers(t, testEnv)
  34. ProtectImages(t, testEnv)
  35. ProtectNetworks(t, testEnv)
  36. ProtectVolumes(t, testEnv)
  37. if testEnv.DaemonInfo.OSType == "linux" {
  38. ProtectPlugins(t, testEnv)
  39. }
  40. }
  41. // ProtectContainer adds the specified container(s) to be protected in case of
  42. // clean
  43. func (e *Execution) ProtectContainer(t testing.TB, containers ...string) {
  44. t.Helper()
  45. for _, container := range containers {
  46. e.protectedElements.containers[container] = struct{}{}
  47. }
  48. }
  49. // ProtectContainers protects existing containers from being cleaned up at the
  50. // end of test runs
  51. func ProtectContainers(t testing.TB, testEnv *Execution) {
  52. t.Helper()
  53. containers := getExistingContainers(t, testEnv)
  54. testEnv.ProtectContainer(t, containers...)
  55. }
  56. func getExistingContainers(t testing.TB, testEnv *Execution) []string {
  57. t.Helper()
  58. client := testEnv.APIClient()
  59. containerList, err := client.ContainerList(context.Background(), types.ContainerListOptions{
  60. All: true,
  61. })
  62. assert.NilError(t, err, "failed to list containers")
  63. var containers []string
  64. for _, container := range containerList {
  65. containers = append(containers, container.ID)
  66. }
  67. return containers
  68. }
  69. // ProtectImage adds the specified image(s) to be protected in case of clean
  70. func (e *Execution) ProtectImage(t testing.TB, images ...string) {
  71. t.Helper()
  72. for _, image := range images {
  73. e.protectedElements.images[image] = struct{}{}
  74. }
  75. }
  76. // ProtectImages protects existing images and on linux frozen images from being
  77. // cleaned up at the end of test runs
  78. func ProtectImages(t testing.TB, testEnv *Execution) {
  79. t.Helper()
  80. images := getExistingImages(t, testEnv)
  81. if testEnv.DaemonInfo.OSType == "linux" {
  82. images = append(images, frozenImages...)
  83. }
  84. testEnv.ProtectImage(t, images...)
  85. testEnv.ProtectImage(t, DanglingImageIdGraphDriver, DanglingImageIdSnapshotter)
  86. }
  87. func getExistingImages(t testing.TB, testEnv *Execution) []string {
  88. t.Helper()
  89. client := testEnv.APIClient()
  90. imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
  91. All: true,
  92. Filters: filters.NewArgs(filters.Arg("dangling", "false")),
  93. })
  94. assert.NilError(t, err, "failed to list images")
  95. var images []string
  96. for _, image := range imageList {
  97. images = append(images, tagsFromImageSummary(image)...)
  98. }
  99. return images
  100. }
  101. func tagsFromImageSummary(image types.ImageSummary) []string {
  102. var result []string
  103. for _, tag := range image.RepoTags {
  104. // Starting from API 1.43 no longer outputs the hardcoded <none>
  105. // strings. But since the tests might be ran against a remote
  106. // daemon/pre 1.43 CLI we must still be able to handle it.
  107. if tag != "<none>:<none>" {
  108. result = append(result, tag)
  109. }
  110. }
  111. for _, digest := range image.RepoDigests {
  112. if digest != "<none>@<none>" {
  113. result = append(result, digest)
  114. }
  115. }
  116. return result
  117. }
  118. // ProtectNetwork adds the specified network(s) to be protected in case of
  119. // clean
  120. func (e *Execution) ProtectNetwork(t testing.TB, networks ...string) {
  121. t.Helper()
  122. for _, network := range networks {
  123. e.protectedElements.networks[network] = struct{}{}
  124. }
  125. }
  126. // ProtectNetworks protects existing networks from being cleaned up at the end
  127. // of test runs
  128. func ProtectNetworks(t testing.TB, testEnv *Execution) {
  129. t.Helper()
  130. networks := getExistingNetworks(t, testEnv)
  131. testEnv.ProtectNetwork(t, networks...)
  132. }
  133. func getExistingNetworks(t testing.TB, testEnv *Execution) []string {
  134. t.Helper()
  135. client := testEnv.APIClient()
  136. networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{})
  137. assert.NilError(t, err, "failed to list networks")
  138. var networks []string
  139. for _, network := range networkList {
  140. networks = append(networks, network.ID)
  141. }
  142. return networks
  143. }
  144. // ProtectPlugin adds the specified plugin(s) to be protected in case of clean
  145. func (e *Execution) ProtectPlugin(t testing.TB, plugins ...string) {
  146. t.Helper()
  147. for _, plugin := range plugins {
  148. e.protectedElements.plugins[plugin] = struct{}{}
  149. }
  150. }
  151. // ProtectPlugins protects existing plugins from being cleaned up at the end of
  152. // test runs
  153. func ProtectPlugins(t testing.TB, testEnv *Execution) {
  154. t.Helper()
  155. plugins := getExistingPlugins(t, testEnv)
  156. testEnv.ProtectPlugin(t, plugins...)
  157. }
  158. func getExistingPlugins(t testing.TB, testEnv *Execution) []string {
  159. t.Helper()
  160. client := testEnv.APIClient()
  161. pluginList, err := client.PluginList(context.Background(), filters.Args{})
  162. // Docker EE does not allow cluster-wide plugin management.
  163. if errdefs.IsNotImplemented(err) {
  164. return []string{}
  165. }
  166. assert.NilError(t, err, "failed to list plugins")
  167. var plugins []string
  168. for _, plugin := range pluginList {
  169. plugins = append(plugins, plugin.Name)
  170. }
  171. return plugins
  172. }
  173. // ProtectVolume adds the specified volume(s) to be protected in case of clean
  174. func (e *Execution) ProtectVolume(t testing.TB, volumes ...string) {
  175. t.Helper()
  176. for _, vol := range volumes {
  177. e.protectedElements.volumes[vol] = struct{}{}
  178. }
  179. }
  180. // ProtectVolumes protects existing volumes from being cleaned up at the end of
  181. // test runs
  182. func ProtectVolumes(t testing.TB, testEnv *Execution) {
  183. t.Helper()
  184. volumes := getExistingVolumes(t, testEnv)
  185. testEnv.ProtectVolume(t, volumes...)
  186. }
  187. func getExistingVolumes(t testing.TB, testEnv *Execution) []string {
  188. t.Helper()
  189. client := testEnv.APIClient()
  190. volumeList, err := client.VolumeList(context.Background(), volume.ListOptions{})
  191. assert.NilError(t, err, "failed to list volumes")
  192. var volumes []string
  193. for _, vol := range volumeList.Volumes {
  194. volumes = append(volumes, vol.Name)
  195. }
  196. return volumes
  197. }