prune.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package daemon
  2. import (
  3. "fmt"
  4. "regexp"
  5. "time"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/distribution/reference"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/api/types/filters"
  10. timetypes "github.com/docker/docker/api/types/time"
  11. "github.com/docker/docker/image"
  12. "github.com/docker/docker/layer"
  13. "github.com/docker/docker/pkg/directory"
  14. "github.com/docker/docker/runconfig"
  15. "github.com/docker/docker/volume"
  16. "github.com/docker/libnetwork"
  17. "github.com/opencontainers/go-digest"
  18. )
  19. // ContainersPrune removes unused containers
  20. func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) {
  21. rep := &types.ContainersPruneReport{}
  22. until, err := getUntilFromPruneFilters(pruneFilters)
  23. if err != nil {
  24. return nil, err
  25. }
  26. allContainers := daemon.List()
  27. for _, c := range allContainers {
  28. if !c.IsRunning() {
  29. if !until.IsZero() && c.Created.After(until) {
  30. continue
  31. }
  32. cSize, _ := daemon.getSize(c)
  33. // TODO: sets RmLink to true?
  34. err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
  35. if err != nil {
  36. logrus.Warnf("failed to prune container %s: %v", c.ID, err)
  37. continue
  38. }
  39. if cSize > 0 {
  40. rep.SpaceReclaimed += uint64(cSize)
  41. }
  42. rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID)
  43. }
  44. }
  45. return rep, nil
  46. }
  47. // VolumesPrune removes unused local volumes
  48. func (daemon *Daemon) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) {
  49. rep := &types.VolumesPruneReport{}
  50. pruneVols := func(v volume.Volume) error {
  51. name := v.Name()
  52. refs := daemon.volumes.Refs(v)
  53. if len(refs) == 0 {
  54. vSize, err := directory.Size(v.Path())
  55. if err != nil {
  56. logrus.Warnf("could not determine size of volume %s: %v", name, err)
  57. }
  58. err = daemon.volumes.Remove(v)
  59. if err != nil {
  60. logrus.Warnf("could not remove volume %s: %v", name, err)
  61. return nil
  62. }
  63. rep.SpaceReclaimed += uint64(vSize)
  64. rep.VolumesDeleted = append(rep.VolumesDeleted, name)
  65. }
  66. return nil
  67. }
  68. err := daemon.traverseLocalVolumes(pruneVols)
  69. return rep, err
  70. }
  71. // ImagesPrune removes unused images
  72. func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
  73. rep := &types.ImagesPruneReport{}
  74. danglingOnly := true
  75. if pruneFilters.Include("dangling") {
  76. if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") {
  77. danglingOnly = false
  78. } else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") {
  79. return nil, fmt.Errorf("Invalid filter 'dangling=%s'", pruneFilters.Get("dangling"))
  80. }
  81. }
  82. until, err := getUntilFromPruneFilters(pruneFilters)
  83. if err != nil {
  84. return nil, err
  85. }
  86. var allImages map[image.ID]*image.Image
  87. if danglingOnly {
  88. allImages = daemon.imageStore.Heads()
  89. } else {
  90. allImages = daemon.imageStore.Map()
  91. }
  92. allContainers := daemon.List()
  93. imageRefs := map[string]bool{}
  94. for _, c := range allContainers {
  95. imageRefs[c.ID] = true
  96. }
  97. // Filter intermediary images and get their unique size
  98. allLayers := daemon.layerStore.Map()
  99. topImages := map[image.ID]*image.Image{}
  100. for id, img := range allImages {
  101. dgst := digest.Digest(id)
  102. if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
  103. continue
  104. }
  105. if !until.IsZero() && img.Created.After(until) {
  106. continue
  107. }
  108. topImages[id] = img
  109. }
  110. for id := range topImages {
  111. dgst := digest.Digest(id)
  112. hex := dgst.Hex()
  113. if _, ok := imageRefs[hex]; ok {
  114. continue
  115. }
  116. deletedImages := []types.ImageDeleteResponseItem{}
  117. refs := daemon.referenceStore.References(dgst)
  118. if len(refs) > 0 {
  119. shouldDelete := !danglingOnly
  120. if !shouldDelete {
  121. hasTag := false
  122. for _, ref := range refs {
  123. if _, ok := ref.(reference.NamedTagged); ok {
  124. hasTag = true
  125. break
  126. }
  127. }
  128. // Only delete if it's untagged (i.e. repo:<none>)
  129. shouldDelete = !hasTag
  130. }
  131. if shouldDelete {
  132. for _, ref := range refs {
  133. imgDel, err := daemon.ImageDelete(ref.String(), false, true)
  134. if err != nil {
  135. logrus.Warnf("could not delete reference %s: %v", ref.String(), err)
  136. continue
  137. }
  138. deletedImages = append(deletedImages, imgDel...)
  139. }
  140. }
  141. } else {
  142. imgDel, err := daemon.ImageDelete(hex, false, true)
  143. if err != nil {
  144. logrus.Warnf("could not delete image %s: %v", hex, err)
  145. continue
  146. }
  147. deletedImages = append(deletedImages, imgDel...)
  148. }
  149. rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...)
  150. }
  151. // Compute how much space was freed
  152. for _, d := range rep.ImagesDeleted {
  153. if d.Deleted != "" {
  154. chid := layer.ChainID(d.Deleted)
  155. if l, ok := allLayers[chid]; ok {
  156. diffSize, err := l.DiffSize()
  157. if err != nil {
  158. logrus.Warnf("failed to get layer %s size: %v", chid, err)
  159. continue
  160. }
  161. rep.SpaceReclaimed += uint64(diffSize)
  162. }
  163. }
  164. }
  165. return rep, nil
  166. }
  167. // localNetworksPrune removes unused local networks
  168. func (daemon *Daemon) localNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
  169. rep := &types.NetworksPruneReport{}
  170. until, err := getUntilFromPruneFilters(pruneFilters)
  171. if err != nil {
  172. return rep, err
  173. }
  174. // When the function returns true, the walk will stop.
  175. l := func(nw libnetwork.Network) bool {
  176. if !until.IsZero() && nw.Info().Created().After(until) {
  177. return false
  178. }
  179. nwName := nw.Name()
  180. predefined := runconfig.IsPreDefinedNetwork(nwName)
  181. if !predefined && len(nw.Endpoints()) == 0 {
  182. if err = daemon.DeleteNetwork(nw.ID()); err != nil {
  183. logrus.Warnf("could not remove network %s: %v", nwName, err)
  184. return false
  185. }
  186. rep.NetworksDeleted = append(rep.NetworksDeleted, nwName)
  187. }
  188. return false
  189. }
  190. daemon.netController.WalkNetworks(l)
  191. return rep, err
  192. }
  193. // clusterNetworksPrune removes unused cluster networks
  194. func (daemon *Daemon) clusterNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
  195. rep := &types.NetworksPruneReport{}
  196. until, err := getUntilFromPruneFilters(pruneFilters)
  197. if err != nil {
  198. return nil, err
  199. }
  200. cluster := daemon.GetCluster()
  201. networks, err := cluster.GetNetworks()
  202. if err != nil {
  203. return rep, err
  204. }
  205. networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`)
  206. for _, nw := range networks {
  207. if nw.Name == "ingress" {
  208. continue
  209. }
  210. if !until.IsZero() && nw.Created.After(until) {
  211. continue
  212. }
  213. // https://github.com/docker/docker/issues/24186
  214. // `docker network inspect` unfortunately displays ONLY those containers that are local to that node.
  215. // So we try to remove it anyway and check the error
  216. err = cluster.RemoveNetwork(nw.ID)
  217. if err != nil {
  218. // we can safely ignore the "network .. is in use" error
  219. match := networkIsInUse.FindStringSubmatch(err.Error())
  220. if len(match) != 2 || match[1] != nw.ID {
  221. logrus.Warnf("could not remove network %s: %v", nw.Name, err)
  222. }
  223. continue
  224. }
  225. rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name)
  226. }
  227. return rep, nil
  228. }
  229. // NetworksPrune removes unused networks
  230. func (daemon *Daemon) NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
  231. rep := &types.NetworksPruneReport{}
  232. clusterRep, err := daemon.clusterNetworksPrune(pruneFilters)
  233. if err != nil {
  234. logrus.Warnf("could not remove cluster networks: %v", err)
  235. } else {
  236. rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...)
  237. }
  238. localRep, err := daemon.localNetworksPrune(pruneFilters)
  239. if err != nil {
  240. logrus.Warnf("could not remove local networks: %v", err)
  241. } else {
  242. rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...)
  243. }
  244. return rep, err
  245. }
  246. func getUntilFromPruneFilters(pruneFilters filters.Args) (time.Time, error) {
  247. until := time.Time{}
  248. if !pruneFilters.Include("until") {
  249. return until, nil
  250. }
  251. untilFilters := pruneFilters.Get("until")
  252. if len(untilFilters) > 1 {
  253. return until, fmt.Errorf("more than one until filter specified")
  254. }
  255. ts, err := timetypes.GetTimestamp(untilFilters[0], time.Now())
  256. if err != nil {
  257. return until, err
  258. }
  259. seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0)
  260. if err != nil {
  261. return until, err
  262. }
  263. until = time.Unix(seconds, nanoseconds)
  264. return until, nil
  265. }