protect.go 6.0 KB

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