protect.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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.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.OSType == "linux" {
  82. images = append(images, frozenImages...)
  83. }
  84. testEnv.ProtectImage(t, images...)
  85. }
  86. func getExistingImages(t testing.TB, testEnv *Execution) []string {
  87. t.Helper()
  88. client := testEnv.APIClient()
  89. filter := filters.NewArgs()
  90. filter.Add("dangling", "false")
  91. imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
  92. All: true,
  93. Filters: filter,
  94. })
  95. assert.NilError(t, err, "failed to list images")
  96. var images []string
  97. for _, image := range imageList {
  98. images = append(images, tagsFromImageSummary(image)...)
  99. }
  100. return images
  101. }
  102. func tagsFromImageSummary(image types.ImageSummary) []string {
  103. var result []string
  104. for _, tag := range image.RepoTags {
  105. if tag != "<none>:<none>" {
  106. result = append(result, tag)
  107. }
  108. }
  109. for _, digest := range image.RepoDigests {
  110. if digest != "<none>@<none>" {
  111. result = append(result, digest)
  112. }
  113. }
  114. return result
  115. }
  116. // ProtectNetwork adds the specified network(s) to be protected in case of
  117. // clean
  118. func (e *Execution) ProtectNetwork(t testing.TB, networks ...string) {
  119. t.Helper()
  120. for _, network := range networks {
  121. e.protectedElements.networks[network] = struct{}{}
  122. }
  123. }
  124. // ProtectNetworks protects existing networks from being cleaned up at the end
  125. // of test runs
  126. func ProtectNetworks(t testing.TB, testEnv *Execution) {
  127. t.Helper()
  128. networks := getExistingNetworks(t, testEnv)
  129. testEnv.ProtectNetwork(t, networks...)
  130. }
  131. func getExistingNetworks(t testing.TB, testEnv *Execution) []string {
  132. t.Helper()
  133. client := testEnv.APIClient()
  134. networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{})
  135. assert.NilError(t, err, "failed to list networks")
  136. var networks []string
  137. for _, network := range networkList {
  138. networks = append(networks, network.ID)
  139. }
  140. return networks
  141. }
  142. // ProtectPlugin adds the specified plugin(s) to be protected in case of clean
  143. func (e *Execution) ProtectPlugin(t testing.TB, plugins ...string) {
  144. t.Helper()
  145. for _, plugin := range plugins {
  146. e.protectedElements.plugins[plugin] = struct{}{}
  147. }
  148. }
  149. // ProtectPlugins protects existing plugins from being cleaned up at the end of
  150. // test runs
  151. func ProtectPlugins(t testing.TB, testEnv *Execution) {
  152. t.Helper()
  153. plugins := getExistingPlugins(t, testEnv)
  154. testEnv.ProtectPlugin(t, plugins...)
  155. }
  156. func getExistingPlugins(t testing.TB, testEnv *Execution) []string {
  157. t.Helper()
  158. client := testEnv.APIClient()
  159. pluginList, err := client.PluginList(context.Background(), filters.Args{})
  160. // Docker EE does not allow cluster-wide plugin management.
  161. if errdefs.IsNotImplemented(err) {
  162. return []string{}
  163. }
  164. assert.NilError(t, err, "failed to list plugins")
  165. var plugins []string
  166. for _, plugin := range pluginList {
  167. plugins = append(plugins, plugin.Name)
  168. }
  169. return plugins
  170. }
  171. // ProtectVolume adds the specified volume(s) to be protected in case of clean
  172. func (e *Execution) ProtectVolume(t testing.TB, volumes ...string) {
  173. t.Helper()
  174. for _, vol := range volumes {
  175. e.protectedElements.volumes[vol] = struct{}{}
  176. }
  177. }
  178. // ProtectVolumes protects existing volumes from being cleaned up at the end of
  179. // test runs
  180. func ProtectVolumes(t testing.TB, testEnv *Execution) {
  181. t.Helper()
  182. volumes := getExistingVolumes(t, testEnv)
  183. testEnv.ProtectVolume(t, volumes...)
  184. }
  185. func getExistingVolumes(t testing.TB, testEnv *Execution) []string {
  186. t.Helper()
  187. client := testEnv.APIClient()
  188. volumeList, err := client.VolumeList(context.Background(), volume.ListOptions{})
  189. assert.NilError(t, err, "failed to list volumes")
  190. var volumes []string
  191. for _, vol := range volumeList.Volumes {
  192. volumes = append(volumes, vol.Name)
  193. }
  194. return volumes
  195. }