list.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "context"
  4. "fmt"
  5. "sort"
  6. "strconv"
  7. "strings"
  8. "github.com/containerd/log"
  9. "github.com/docker/docker/api/types"
  10. containertypes "github.com/docker/docker/api/types/container"
  11. "github.com/docker/docker/api/types/filters"
  12. imagetypes "github.com/docker/docker/api/types/image"
  13. "github.com/docker/docker/container"
  14. "github.com/docker/docker/errdefs"
  15. "github.com/docker/docker/image"
  16. "github.com/docker/go-connections/nat"
  17. "github.com/pkg/errors"
  18. )
  19. var acceptedPsFilterTags = map[string]bool{
  20. "ancestor": true,
  21. "before": true,
  22. "exited": true,
  23. "id": true,
  24. "isolation": true,
  25. "label": true,
  26. "name": true,
  27. "status": true,
  28. "health": true,
  29. "since": true,
  30. "volume": true,
  31. "network": true,
  32. "is-task": true,
  33. "publish": true,
  34. "expose": true,
  35. }
  36. // iterationAction represents possible outcomes happening during the container iteration.
  37. type iterationAction int
  38. const (
  39. // includeContainer is the action to include a container.
  40. includeContainer iterationAction = iota
  41. // excludeContainer is the action to exclude a container.
  42. excludeContainer
  43. // stopIteration is the action to stop iterating over the list of containers.
  44. stopIteration
  45. )
  46. // List returns an array of all containers registered in the daemon.
  47. func (daemon *Daemon) List() []*container.Container {
  48. return daemon.containers.List()
  49. }
  50. // listContext is the daemon generated filtering to iterate over containers.
  51. // This is created based on the user specification from [containertypes.ListOptions].
  52. type listContext struct {
  53. // idx is the container iteration index for this context
  54. idx int
  55. // ancestorFilter tells whether it should check ancestors or not
  56. ancestorFilter bool
  57. // names is a list of container names to filter with
  58. names map[string][]string
  59. // images is a list of images to filter with
  60. images map[image.ID]bool
  61. // filters is a collection of arguments to filter with, specified by the user
  62. filters filters.Args
  63. // exitAllowed is a list of exit codes allowed to filter with
  64. exitAllowed []int
  65. // beforeFilter is a filter to ignore containers that appear before the one given
  66. beforeFilter *container.Snapshot
  67. // sinceFilter is a filter to stop the filtering when the iterator arrives to the given container
  68. sinceFilter *container.Snapshot
  69. // taskFilter tells if we should filter based on whether a container is part of a task
  70. taskFilter bool
  71. // isTask tells us if we should filter container that is a task (true) or not (false)
  72. isTask bool
  73. // publish is a list of published ports to filter with
  74. publish map[nat.Port]bool
  75. // expose is a list of exposed ports to filter with
  76. expose map[nat.Port]bool
  77. // ListOptions is the filters set by the user
  78. *containertypes.ListOptions
  79. }
  80. // byCreatedDescending is a temporary type used to sort a list of containers by creation time.
  81. type byCreatedDescending []container.Snapshot
  82. func (r byCreatedDescending) Len() int { return len(r) }
  83. func (r byCreatedDescending) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
  84. func (r byCreatedDescending) Less(i, j int) bool {
  85. return r[j].CreatedAt.UnixNano() < r[i].CreatedAt.UnixNano()
  86. }
  87. // Containers returns the list of containers to show given the user's filtering.
  88. func (daemon *Daemon) Containers(ctx context.Context, config *containertypes.ListOptions) ([]*types.Container, error) {
  89. if err := config.Filters.Validate(acceptedPsFilterTags); err != nil {
  90. return nil, err
  91. }
  92. var (
  93. view = daemon.containersReplica.Snapshot()
  94. containers = []*types.Container{}
  95. )
  96. filter, err := daemon.foldFilter(ctx, view, config)
  97. if err != nil {
  98. return nil, err
  99. }
  100. // fastpath to only look at a subset of containers if specific name
  101. // or ID matches were provided by the user--otherwise we potentially
  102. // end up querying many more containers than intended
  103. containerList, err := daemon.filterByNameIDMatches(view, filter)
  104. if err != nil {
  105. return nil, err
  106. }
  107. for i := range containerList {
  108. currentContainer := &containerList[i]
  109. switch includeContainerInList(currentContainer, filter) {
  110. case excludeContainer:
  111. continue
  112. case stopIteration:
  113. return containers, nil
  114. }
  115. // transform internal container struct into api structs
  116. newC, err := daemon.refreshImage(ctx, currentContainer)
  117. if err != nil {
  118. return nil, err
  119. }
  120. // release lock because size calculation is slow
  121. if filter.Size {
  122. sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, newC.ID)
  123. if err != nil {
  124. return nil, err
  125. }
  126. newC.SizeRw = sizeRw
  127. newC.SizeRootFs = sizeRootFs
  128. }
  129. if newC != nil {
  130. containers = append(containers, newC)
  131. filter.idx++
  132. }
  133. }
  134. return containers, nil
  135. }
  136. func (daemon *Daemon) filterByNameIDMatches(view *container.View, filter *listContext) ([]container.Snapshot, error) {
  137. idSearch := false
  138. names := filter.filters.Get("name")
  139. ids := filter.filters.Get("id")
  140. if len(names)+len(ids) == 0 {
  141. // if name or ID filters are not in use, return to
  142. // standard behavior of walking the entire container
  143. // list from the daemon's in-memory store
  144. all, err := view.All()
  145. if err != nil {
  146. return nil, err
  147. }
  148. sort.Sort(byCreatedDescending(all))
  149. return all, nil
  150. }
  151. // idSearch will determine if we limit name matching to the IDs
  152. // matched from any IDs which were specified as filters
  153. if len(ids) > 0 {
  154. idSearch = true
  155. }
  156. matches := make(map[string]bool)
  157. // find ID matches; errors represent "not found" and can be ignored
  158. for _, id := range ids {
  159. if fullID, err := daemon.containersReplica.GetByPrefix(id); err == nil {
  160. matches[fullID] = true
  161. }
  162. }
  163. // look for name matches; if ID filtering was used, then limit the
  164. // search space to the matches map only; errors represent "not found"
  165. // and can be ignored
  166. if len(names) > 0 {
  167. for id, idNames := range filter.names {
  168. // if ID filters were used and no matches on that ID were
  169. // found, continue to next ID in the list
  170. if idSearch && !matches[id] {
  171. continue
  172. }
  173. for _, eachName := range idNames {
  174. // match both on container name with, and without slash-prefix
  175. if filter.filters.Match("name", eachName) || filter.filters.Match("name", strings.TrimPrefix(eachName, "/")) {
  176. matches[id] = true
  177. }
  178. }
  179. }
  180. }
  181. cntrs := make([]container.Snapshot, 0, len(matches))
  182. for id := range matches {
  183. c, err := view.Get(id)
  184. if err != nil {
  185. if errdefs.IsNotFound(err) {
  186. // ignore error
  187. continue
  188. }
  189. return nil, err
  190. }
  191. cntrs = append(cntrs, *c)
  192. }
  193. // Restore sort-order after filtering
  194. // Created gives us nanosec resolution for sorting
  195. sort.Sort(byCreatedDescending(cntrs))
  196. return cntrs, nil
  197. }
  198. // foldFilter generates the container filter based on the user's filtering options.
  199. func (daemon *Daemon) foldFilter(ctx context.Context, view *container.View, config *containertypes.ListOptions) (*listContext, error) {
  200. psFilters := config.Filters
  201. var filtExited []int
  202. err := psFilters.WalkValues("exited", func(value string) error {
  203. code, err := strconv.Atoi(value)
  204. if err != nil {
  205. return errdefs.InvalidParameter(errors.Wrapf(err, "invalid filter 'exited=%s'", value))
  206. }
  207. filtExited = append(filtExited, code)
  208. return nil
  209. })
  210. if err != nil {
  211. return nil, err
  212. }
  213. err = psFilters.WalkValues("status", func(value string) error {
  214. if !container.IsValidStateString(value) {
  215. return errdefs.InvalidParameter(fmt.Errorf("invalid filter 'status=%s'", value))
  216. }
  217. config.All = true
  218. return nil
  219. })
  220. if err != nil {
  221. return nil, err
  222. }
  223. taskFilter := psFilters.Contains("is-task")
  224. isTask, err := psFilters.GetBoolOrDefault("is-task", false)
  225. if err != nil {
  226. return nil, err
  227. }
  228. err = psFilters.WalkValues("health", func(value string) error {
  229. if !container.IsValidHealthString(value) {
  230. return errdefs.InvalidParameter(fmt.Errorf("unrecognized filter value for health: %s", value))
  231. }
  232. return nil
  233. })
  234. if err != nil {
  235. return nil, err
  236. }
  237. var beforeContFilter, sinceContFilter *container.Snapshot
  238. err = psFilters.WalkValues("before", func(value string) error {
  239. beforeContFilter, err = idOrNameFilter(view, value)
  240. return err
  241. })
  242. if err != nil {
  243. return nil, err
  244. }
  245. err = psFilters.WalkValues("since", func(value string) error {
  246. sinceContFilter, err = idOrNameFilter(view, value)
  247. return err
  248. })
  249. if err != nil {
  250. return nil, err
  251. }
  252. imagesFilter := map[image.ID]bool{}
  253. var ancestorFilter bool
  254. if psFilters.Contains("ancestor") {
  255. ancestorFilter = true
  256. err := psFilters.WalkValues("ancestor", func(ancestor string) error {
  257. img, err := daemon.imageService.GetImage(ctx, ancestor, imagetypes.GetImageOpts{})
  258. if err != nil {
  259. log.G(ctx).Warnf("Error while looking up for image %v", ancestor)
  260. return nil
  261. }
  262. if imagesFilter[img.ID()] {
  263. // Already seen this ancestor, skip it
  264. return nil
  265. }
  266. // Then walk down the graph and put the imageIds in imagesFilter
  267. return populateImageFilterByParents(ctx, imagesFilter, img.ID(), daemon.imageService.Children)
  268. })
  269. if err != nil {
  270. return nil, err
  271. }
  272. }
  273. publishFilter := map[nat.Port]bool{}
  274. err = psFilters.WalkValues("publish", portOp("publish", publishFilter))
  275. if err != nil {
  276. return nil, err
  277. }
  278. exposeFilter := map[nat.Port]bool{}
  279. err = psFilters.WalkValues("expose", portOp("expose", exposeFilter))
  280. if err != nil {
  281. return nil, err
  282. }
  283. return &listContext{
  284. filters: psFilters,
  285. ancestorFilter: ancestorFilter,
  286. images: imagesFilter,
  287. exitAllowed: filtExited,
  288. beforeFilter: beforeContFilter,
  289. sinceFilter: sinceContFilter,
  290. taskFilter: taskFilter,
  291. isTask: isTask,
  292. publish: publishFilter,
  293. expose: exposeFilter,
  294. ListOptions: config,
  295. names: view.GetAllNames(),
  296. }, nil
  297. }
  298. func idOrNameFilter(view *container.View, value string) (*container.Snapshot, error) {
  299. filter, err := view.Get(value)
  300. if err != nil && errdefs.IsNotFound(err) {
  301. // Try name search instead
  302. found := ""
  303. for id, idNames := range view.GetAllNames() {
  304. for _, eachName := range idNames {
  305. if strings.TrimPrefix(value, "/") == strings.TrimPrefix(eachName, "/") {
  306. if found != "" && found != id {
  307. return nil, err
  308. }
  309. found = id
  310. }
  311. }
  312. }
  313. if found != "" {
  314. filter, err = view.Get(found)
  315. }
  316. }
  317. return filter, err
  318. }
  319. func portOp(key string, filter map[nat.Port]bool) func(value string) error {
  320. return func(value string) error {
  321. if strings.Contains(value, ":") {
  322. return fmt.Errorf("filter for '%s' should not contain ':': %s", key, value)
  323. }
  324. // support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
  325. proto, port := nat.SplitProtoPort(value)
  326. start, end, err := nat.ParsePortRange(port)
  327. if err != nil {
  328. return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
  329. }
  330. for i := start; i <= end; i++ {
  331. p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
  332. if err != nil {
  333. return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
  334. }
  335. filter[p] = true
  336. }
  337. return nil
  338. }
  339. }
  340. // includeContainerInList decides whether a container should be included in the output or not based in the filter.
  341. // It also decides if the iteration should be stopped or not.
  342. func includeContainerInList(container *container.Snapshot, filter *listContext) iterationAction {
  343. // Do not include container if it's in the list before the filter container.
  344. // Set the filter container to nil to include the rest of containers after this one.
  345. if filter.beforeFilter != nil {
  346. if container.ID == filter.beforeFilter.ID {
  347. filter.beforeFilter = nil
  348. }
  349. return excludeContainer
  350. }
  351. // Stop iteration when the container arrives to the filter container
  352. if filter.sinceFilter != nil {
  353. if container.ID == filter.sinceFilter.ID {
  354. return stopIteration
  355. }
  356. }
  357. // Do not include container if it's stopped and we're not filters
  358. if !container.Running && !filter.All && filter.Limit <= 0 {
  359. return excludeContainer
  360. }
  361. // Do not include container if the name doesn't match
  362. if !filter.filters.Match("name", container.Name) && !filter.filters.Match("name", strings.TrimPrefix(container.Name, "/")) {
  363. return excludeContainer
  364. }
  365. // Do not include container if the id doesn't match
  366. if !filter.filters.Match("id", container.ID) {
  367. return excludeContainer
  368. }
  369. if filter.taskFilter {
  370. if filter.isTask != container.Managed {
  371. return excludeContainer
  372. }
  373. }
  374. // Do not include container if any of the labels don't match
  375. if !filter.filters.MatchKVList("label", container.Labels) {
  376. return excludeContainer
  377. }
  378. // Do not include container if isolation doesn't match
  379. if excludeContainer == excludeByIsolation(container, filter) {
  380. return excludeContainer
  381. }
  382. // Stop iteration when the index is over the limit
  383. if filter.Limit > 0 && filter.idx == filter.Limit {
  384. return stopIteration
  385. }
  386. // Do not include container if its exit code is not in the filter
  387. if len(filter.exitAllowed) > 0 {
  388. shouldSkip := true
  389. for _, code := range filter.exitAllowed {
  390. if code == container.ExitCode && !container.Running && !container.StartedAt.IsZero() {
  391. shouldSkip = false
  392. break
  393. }
  394. }
  395. if shouldSkip {
  396. return excludeContainer
  397. }
  398. }
  399. // Do not include container if its status doesn't match the filter
  400. if !filter.filters.Match("status", container.State) {
  401. return excludeContainer
  402. }
  403. // Do not include container if its health doesn't match the filter
  404. if !filter.filters.ExactMatch("health", container.Health) {
  405. return excludeContainer
  406. }
  407. if filter.filters.Contains("volume") {
  408. volumesByName := make(map[string]types.MountPoint)
  409. for _, m := range container.Mounts {
  410. if m.Name != "" {
  411. volumesByName[m.Name] = m
  412. } else {
  413. volumesByName[m.Source] = m
  414. }
  415. }
  416. volumesByDestination := make(map[string]types.MountPoint)
  417. for _, m := range container.Mounts {
  418. if m.Destination != "" {
  419. volumesByDestination[m.Destination] = m
  420. }
  421. }
  422. volumeExist := fmt.Errorf("volume mounted in container")
  423. err := filter.filters.WalkValues("volume", func(value string) error {
  424. if _, exist := volumesByDestination[value]; exist {
  425. return volumeExist
  426. }
  427. if _, exist := volumesByName[value]; exist {
  428. return volumeExist
  429. }
  430. return nil
  431. })
  432. if err != volumeExist {
  433. return excludeContainer
  434. }
  435. }
  436. if filter.ancestorFilter {
  437. if len(filter.images) == 0 {
  438. return excludeContainer
  439. }
  440. if !filter.images[image.ID(container.ImageID)] {
  441. return excludeContainer
  442. }
  443. }
  444. var (
  445. networkExist = errors.New("container part of network")
  446. noNetworks = errors.New("container is not part of any networks")
  447. )
  448. if filter.filters.Contains("network") {
  449. err := filter.filters.WalkValues("network", func(value string) error {
  450. if container.NetworkSettings == nil {
  451. return noNetworks
  452. }
  453. if _, ok := container.NetworkSettings.Networks[value]; ok {
  454. return networkExist
  455. }
  456. for _, nw := range container.NetworkSettings.Networks {
  457. if nw == nil {
  458. continue
  459. }
  460. if strings.HasPrefix(nw.NetworkID, value) {
  461. return networkExist
  462. }
  463. }
  464. return nil
  465. })
  466. if err != networkExist {
  467. return excludeContainer
  468. }
  469. }
  470. if len(filter.expose) > 0 || len(filter.publish) > 0 {
  471. var (
  472. shouldSkip = true
  473. publishedPort nat.Port
  474. exposedPort nat.Port
  475. )
  476. for _, port := range container.Ports {
  477. publishedPort = nat.Port(fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
  478. exposedPort = nat.Port(fmt.Sprintf("%d/%s", port.PrivatePort, port.Type))
  479. if ok := filter.publish[publishedPort]; ok {
  480. shouldSkip = false
  481. break
  482. } else if ok := filter.expose[exposedPort]; ok {
  483. shouldSkip = false
  484. break
  485. }
  486. }
  487. if shouldSkip {
  488. return excludeContainer
  489. }
  490. }
  491. return includeContainer
  492. }
  493. // refreshImage checks if the Image ref still points to the correct ID, and
  494. // updates the ref to the actual ID when it doesn't.
  495. // This happens when the image with a reference that was used to create
  496. // container was deleted or updated and now resolves to a different ID.
  497. //
  498. // For example:
  499. // $ docker run -d busybox:latest
  500. // $ docker ps -a
  501. // CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  502. // b0318bca5aef busybox "sh" 4 seconds ago Exited (0) 3 seconds ago ecstatic_beaver
  503. //
  504. // After some time, busybox image got updated on the Docker Hub:
  505. // $ docker pull busybox:latest
  506. //
  507. // So now busybox:latest points to a different digest, but that doesn't impact
  508. // the ecstatic_beaver container which was still created under an older
  509. // version. In this case, it should still point to the original image ID it was
  510. // created from.
  511. //
  512. // $ docker ps -a
  513. // CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  514. // b0318bca5aef 3fbc63216742 "sh" 3 years ago Exited (0) 3 years ago ecstatic_beaver
  515. func (daemon *Daemon) refreshImage(ctx context.Context, s *container.Snapshot) (*types.Container, error) {
  516. c := s.Container
  517. // s.Image is the image reference passed by the user to create an image
  518. // can be a:
  519. // - name (like nginx, ubuntu:latest, docker.io/library/busybox:latest),
  520. // - truncated ID (abcdef),
  521. // - full digest (sha256:abcdef...)
  522. //
  523. // s.ImageID is the ID of the image that s.Image resolved to at the time
  524. // of the container creation. It's always a full digest.
  525. // If these match, there's nothing to refresh.
  526. if s.Image == s.ImageID {
  527. return &c, nil
  528. }
  529. // Check if the image reference still resolves to the same digest.
  530. img, err := daemon.imageService.GetImage(ctx, s.Image, imagetypes.GetImageOpts{})
  531. // If the image is no longer found or can't be resolved for some other
  532. // reason. Update the Image to the specific ID of the original image it
  533. // resolved to when the container was created.
  534. if err != nil {
  535. if !errdefs.IsNotFound(err) {
  536. log.G(ctx).WithFields(log.Fields{
  537. "error": err,
  538. "containerID": c.ID,
  539. "image": s.Image,
  540. "imageID": s.ImageID,
  541. }).Warn("failed to resolve container image")
  542. }
  543. c.Image = s.ImageID
  544. return &c, nil
  545. }
  546. // Also update the image to the specific image ID, if the Image now
  547. // resolves to a different ID.
  548. if img.ImageID() != s.ImageID {
  549. c.Image = s.ImageID
  550. }
  551. return &c, nil
  552. }
  553. func populateImageFilterByParents(ctx context.Context, ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(context.Context, image.ID) ([]image.ID, error)) error {
  554. if !ancestorMap[imageID] {
  555. children, err := getChildren(ctx, imageID)
  556. if err != nil {
  557. return err
  558. }
  559. for _, id := range children {
  560. if err := populateImageFilterByParents(ctx, ancestorMap, id, getChildren); err != nil {
  561. return err
  562. }
  563. }
  564. ancestorMap[imageID] = true
  565. }
  566. return nil
  567. }